SpringCloud请求响应数据转换(二)
上篇文章记录了从后端接口返回数据经过切面和消息转换器处理后返回给前端的过程。接下来,记录从请求发出后到后端接口调用过的过程。
web请求处理流程
源码分析
ApplicationFilterChain会调DispatcherServlet类的doService()(HttpServlet类),类继承关系如下:
最终会调DispatcherServlet类的doDispatch方法,并由该方法控制web请求的全过程,包括确定请求方法、确定请求处理适配器和请求实际调用和数据处理,代码如下:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order. 按序遍历并确定HadlerMapping
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class. 找到第一个支持处理类的HandlerAdapters
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable. 所有HTTP请求都由该方法处理,然后由具体的HandlerAdapter和处理类确定调用方法
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//1、获取HandlerMethod
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//2、确定适配器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.判断是否支持If-Modified-Since
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
} if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//3、请求实际处理,包括请求参数的处理、后台接口的调用和返回数据的处理
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
1、获取HandlerMethod
首先是DispatcherServlet的 getHandler方法,获取处理器链,所有处理器(HandlerMapping)都注册在handlerMappings中,如下图所示。
/**
* Return the HandlerExecutionChain for this request.返回处理器链,处理该请求
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍历所有处理器,如下图
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
然后,从前往后遍历所有HandlerMapping,直到handler不为空(14-17行)。
对GET请求,确定HandlerMapping为RequestMappingHandlerMapping(继承自AbstractHandlerMapping),其getHandler方法如下:
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取实际处理方法,如public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String),具体获取方式,见下边 获取HandlerMethod
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//获取处理器执行链条,包含拦截器等,如下图
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
获取HandlerMethod
上边getHandlerInternal方法会调AbstractHandlerMethodMapping类的getHandlerInternal,如下:
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求路径,如/tasks
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
//①获取请求处理方法HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
//②根据HandlerMethod解析容器中对应的bean(控制层bean)
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
①获取请求处理方法HandlerMethod
HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request)方法,根据uri寻找与之匹配的HandlerMethod
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//lookupPath=/tasks,获取与请求uri匹配的接口信息,如 [{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}],其中MappingRegistry mappingRegistry包含了系统所有uri和接口信息。
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
//遍历得到的uri,根据请求信息,如GET方法等,选择匹配的uri({[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}),在mappingRegistry中获取匹配的HandlerMethod,包含后台接口详细信息,如下图。
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
} if (!matches.isEmpty()) {
//对所有匹配的接口进行排序,并使用第一个(排序规则后续再研究)
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
其中,第13行为根据lookupPath(/tasks)获取接口信息,第16行根据接口信息获取后台接口和bean等信息,所有这些信息都存储在内部类MappingRegistry对象中。并且中间会构建一个Match对象,包含所有匹配的接口,并选择第一个作为实际处理接口。MappingRegistry内部类如下所示:
/**
* A registry that maintains all mappings to handler methods, exposing methods
* to perform lookups and providing concurrent access.
*
* <p>Package-private for testing purposes.
*/
class MappingRegistry {
//控制层uri接口信息注册
private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();
//存储uri接口信息和HandlerMethod,如{{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}=public com.service.entity.TaskVO com.service.controller.TaskController.addTask(java.lang.String) throws com.service.exception.BizException, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}=public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String)}
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
//存储uri和uri接口信息(一对多关系),如:{/tasks=[{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}]} private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>(); private final Map<String, List<HandlerMethod>> nameLookup =
new ConcurrentHashMap<String, List<HandlerMethod>>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup =
new ConcurrentHashMap<HandlerMethod, CorsConfiguration>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /**
* Return all mappings and handler methods. Not thread-safe.
* @see #acquireReadLock()
*/
public Map<T, HandlerMethod> getMappings() {
return this.mappingLookup;
} /**
* Return matches for the given URL path. Not thread-safe.
* @see #acquireReadLock()
*/
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
...........
}
②根据HandlerMethod解析容器中对应的bean(控制层bean)
根据上一步得到HandlerMethod,其中bean为bean的名字,将其替换成容器中的bean(控制层对应的bean),调HandlerMethod的createWithResolvedBean方法,如下:
/**
* If the provided instance contains a bean name rather than an object instance,
* the bean name is resolved before a {@link HandlerMethod} is created and returned.
*/
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
其中handler为控制层对应的bean,如下图:
最后,重新构建HandlerMethod,用真实的bean替换掉原来的bean名。
另外,上边涉及的HandlerMapping的类结构如下:
2、确定适配器
存在3种适配器,存储在handlerAdapters中,如下图。
DispatcherServlet方法getHandlerAdapter,根据上一步获取到的处理器HandlerMethod,确定匹配的适配器,代码如下:
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍历所有适配器,如下图。其中handler值为public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String) ,判断适配器是否支持该接口,在本例中RequestMappingHandlerAdapter支持
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//判断是否支持,代码见下边
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
在GET请求中,由于使用注解@RequestMapping,获取到适配器为:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter(继承自AbstractHandlerMethodAdapter),support方法如下:
AbstractHandlerMethodAdapter
1 /**
* This implementation expects the handler to be an {@link HandlerMethod}.
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
RequestMappingHandlerAdapter的supportsInternal方法总返回true,因为接口方法参数和返回值可能存在其他的处理,参数可由HandlerMethodArgumentResolver处理(见后续文章),返回值可由HandlerMethodReturnValueHandler处理(见上篇)
/**
* Always return {@code true} since any method argument and return value
* type will be processed in some way. A method argument not recognized
* by any HandlerMethodArgumentResolver is interpreted as a request parameter
* if it is a simple type, or as a model attribute otherwise. A return value
* not recognized by any HandlerMethodReturnValueHandler will be interpreted
* as a model attribute.
*/
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
最终适配器返回结果如下:
3、请求实际处理,包括请求参数的处理、后台接口的调用和返回数据的处理
调RequestMappingHandlerAdapter的handle方法,对请求进行处理,会调ServletInvocableHandlerMethod的invokeAndHandle方法,其控制整个请求和响应返回的过程。
/**
* Invokes the method and handles the return value through one of the
* configured {@link HandlerMethodReturnValueHandler}s.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//①请求对应的方法,底层采用反射的方式(通过HandleMethod获取控制层的方法和bean,实现反射。第一步已获取到HandleMethod)
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest); if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
} mavContainer.setRequestHandled(false);
try {
//②对方法返回的数据,进行处理,包括切面处理和数据转换(如json)
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
①请求对应的方法,底层采用反射的方式
解析请求参数,调后台接口,返回结果数据,代码如下:
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
* The {@code providedArgs} parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
*/
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取并处理请求参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
//反射调用HandlerMethod中bean对应的接口
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
②对方法返回的数据,进行处理,包括切面处理和数据转换
参见上篇文章SpringCloud请求响应数据转换(一)
请求过程涉及的类
SpringCloud请求响应数据转换(二)的更多相关文章
- SpringCloud请求响应数据转换(一)
异常现象 近期做Spring Cloud项目,工程中对Controller添加ResponseBodyAdvice切面,在切片中将返回的结果封装到ResultMessage(自定义结构),但在Cont ...
- 一比一还原axios源码(二)—— 请求响应处理
上一章,我们开发了一些简单的代码,这部分代码最最核心的一个方法就是buildURL,应对了把对象处理成query参数的方方面面.虽然我们现在可以发起简单的请求了,但是第一,我们无法接收到服务器的响应, ...
- NetMQ(二): 请求响应模式 Request-Reply
ZeroMQ系列 之NetMQ 一:zeromq简介 二:NetMQ 请求响应模式 Request-Reply 三:NetMQ 发布订阅模式 Publisher-Subscriber 四:NetMQ ...
- Flask框架(二)—— 反向解析、配置信息、路由系统、模板、请求响应、闪现、session
Flask框架(二)—— 反向解析.配置信息.路由系统.模板.请求响应.闪现.session 目录 反向解析.配置信息.路由系统.模板.请求响应.闪现.session 一.反向解析 1.什么是反向解析 ...
- 从零搭建Spring Cloud Gateway网关(二)—— 打印请求响应日志
作为网关,日志记录是必不可少的功能,可以在网关出增加requestId来查询整个请求链的调用执行情况等等. 打印请求日志 打印请求日志最重要的就是打印请求参数这些东西,不过RequestBody通常情 ...
- 浅谈WCF的三种通信模式:请求响应模式、数据报模式和双工通讯模式
一: WCF的服务端与客户端在通信时有三种模式:请求响应模式.数据报模式和双工通讯模式. 说一下基本知识, 1.如果想要将当前接口作为wcf服务器,则一定要加上[ServiceContract] 契 ...
- Web请求响应简单整理
简单对Web请求响应如何处理进行的整理,难免有理解不到位,理解有偏差的地方,如有理解有误的地方,希望大牛批评指正. 1.Web开发的定义首先看看微软对Web开发的定义:Web开发是一个指代网页或网 ...
- Ajax的基本请求/响应模型
一.Ajax工作核心 Ajax的核心是JavaScript对象XMLHttpRequest(简称XHR).它是一种支持异步请求的技术.可以通过使用XHR对象向服务器提出请求并处理响应,而不阻塞用户. ...
- HTTP请求响应机制与响应状态码
转载来源:http://blog.csdn.net/xyw591238/article/details/51907143 HTTP协议 Internate的基本协议是TCP/IP(传输控制协议和网际协 ...
随机推荐
- linux:查看以及管理进程
学习笔记内容概要 进程查看的命令:top,ps,pstree 进程管理的命令:kill,nice,renice 查看进程: 一.top工具 top 工具是我们常用的一个查看工具,能实时的查看我们系统的 ...
- Linux执行Cron Job失败,在Shell sh下执行却能成功 - 环境变量?
博客分类: Linux linuxcrontabpermissionetc/profile环境变量 一.我们常常碰到在shell下执行某个命令能够成功,比如执行一个java程序: java -jar ...
- 实例Python处理XML文件的方法
转自:http://www.jb51.net/article/71773.htm 以下是部分代码: <?xml version="1.0" encoding="UT ...
- sql中charindex的用法
转自:https://www.cnblogs.com/beeone/p/3621743.html CHARINDEX和PATINDEX函数常常用来在一段字符中搜索字符或者字符串.如果被搜索的字符中包含 ...
- Linux下设置python脚本文件为服务
(最简单的方式nohup python xxx.py) ------------------------------------------------------------------------ ...
- Django中配置用Redis做缓存和session
django-redis文档: http://django-redis-chs.readthedocs.io/zh_CN/latest/# 一.在Django中配置 # Django的缓存配置 CAC ...
- NYOJ 587 blockhouses 【DFS】
blockhouses 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描写叙述 Suppose that we have a square city with straigh ...
- 008-jdk1.7版本新特性
一.JDK1.7 名称:Dolphin(海豚) 发布日期:2011-07-28 新特性: 1.1.switch-case中可以使用字串 区分大小写.Java编译器通过switch使用String对象的 ...
- POJ:2528(Mayor's posters)离散化成段更新+简单哈希
http://poj.org/problem?id=2528 Description The citizens of Bytetown, AB, could not stand that the ca ...
- hdu1542 Atlantis(矩阵面积的并)
这个题算是我的第一个扫描线的题,扫描线算是一种思想吧,用到线段树+离散化.感觉高大上. 主要参考了这位大神的博客. http://www.cnblogs.com/kuangbin/archive/20 ...