RequestMappingHandlerAdapter
而另一个重要的配置就是处理器适配器 RequestMappingHandlerAdapter
,由于它的继承体系与 RequestMappingHandler
类似,所以我们直接来看它在加载时执行的方法
RequestMappingHandlerAdapter#afterPropertiesSet
public void afterPropertiesSet() { // 首先执行这个方法,可以添加 responseBody 切面 bean initControllerAdviceCache(); // 参数处理器 if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 处理 initBinder 注解 if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 初始化结果处理器 if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
所以看到这个适配器中,初始化了很多工具变量,用来处理 @ControllerAdvice
、InitBinder
等注解和参数。不过核心还是待会要讲到的 handleInternal()
方法,它将适配处理器调用,然后返回 ModelView
视图。
DispatcherServlet 的逻辑处理
请求处理的入口定义在 HttpServlet
,主要有以下几个方法:
当然,父类 HttpServlet
只是给出了定义,直接调用父类这些方法将会报错,所以 FrameworkServlet
将它们覆盖重写了处理逻辑:
protected final void doGet(HttpServletRequest request, HttpServletResponse response) { // 注解 10. 具体调用的是 processRequest 方法 processRequest(request, response); } protected final void doPost(HttpServletRequest request, HttpServletResponse response) { processRequest(request, response); }
可以看到 doGet
、doPost
这些方法,底层调用的都是 processRequest
方法进行处理,关键方法是委托给子类 DispatcherServlet
的 doServie()
方法
DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); // 暂存请求参数 Map<String, Object> attributesSnapshot = null; ... // 经过前面的准备(属性、辅助变量),进入请求处理过程 doDispatch(request, response); }
请求分发和处理逻辑的核心是在 doDispatch(request, response)
方法中,在进入这个方法前,还有些准备工作需要执行。
请求上下文
在 processRequest
的 doServie()
方法执行前,主要做了这以下准备工作:
(1) 为了保证当前线程的 LocaleContext
以及 RequestAttributes
可以在当前请求后还能恢复,提取当前线程的两个属性。
(2) 根据当前 request
创建对应的 LocaleContext
以及 RequestAttributes
,绑定到当前线程
(3) 往 request
对象中设置之前加载过的 localeResolver
、flashMapManager
等辅助工具变量
请求分发 doDispatch
经过前面的配置设置,doDispatch
函数展示了请求的完成处理过程:
DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; // 注释 10. 检查是否 MultipartContent 类型 processedRequest = checkMultipart(request); // 根据 request 信息寻找对应的 Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { // 没有找到 handler,通过 response 向用户返回错误信息 noHandlerFound(processedRequest, response); return; } // 根据当前的 handler 找到对应的 HandlerAdapter 适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 如果当前 handler 支持 last-modified 头处理 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 拦截器的 preHandler 方法的调用 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正激活 handler 进行处理,并返回视图 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 视图名称转换(有可能需要加上前后缀) applyDefaultViewName(processedRequest, mv); // 应用所有拦截器的 postHandle 方法 mappedHandler.applyPostHandle(processedRequest, response, mv); // 处理分发的结果(如果有 mv,进行视图渲染和跳转) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
上面贴出来的代码略有缩减,不过从上面示例中能看出,整体的逻辑都挺清晰的,主要步骤如下:
1. 寻找处理器 mappedandler
2. 根据处理器,寻找对应的适配器 HandlerAdapter
3. 激活 handler
,调用处理方法
4. 返回结果(如果有 mv,进行视图渲染和跳转)
寻找处理器 mappedHandler
以 demo
说明,寻找处理器,就是根据 URL
找到对应的 Controller
方法
DispatcherServlet#getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍历注册的全部 handlerMapping for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
实际上,在这一步遍历了所有注册的 HandlerMapping
,然后委派它们去寻找处理器,如果找到了合适的,就不再往下寻找,直接返回。
同时,HandlerMapping
之间有优先级的概念,根据 mvc
包下 AnnotationDrivenBeanDefinitionParser
的注释:
This class registers the following {@link HandlerMapping HandlerMappings}
@link RequestMappingHandlerMapping
ordered at 0 for mapping requests to annotated controller methods.
说明了 RequestMappingHandlerMapping
的优先级是最高的,优先使用它来寻找适配器。
具体寻找调用的方法:
AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根据 Request 获取对应的 handler Object handler = getHandlerInternal(request); // 将配置中的对应拦截器加入到执行链中,以保证这些拦截器可以有效地作用于目标对象 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (hasCorsConfigurationSource(handler)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
(1) getHandlerInternal(request)
函数作用:
根据 request
信息获取对应的 Handler
,也就是我们例子中的,通过 URL
找到匹配的 Controller
并返回。
(2) getHandlerExcetionChain
函数作用:
将适应该 URL
对应拦截器 MappedInterceptor
加入 addInterceptor()
到执行链 HandlerExecutionChain
中。
(3) CorsConfiguration
这个参数涉及到跨域设置,具体看下这篇文章:SpringBoot下如何配置实现跨域请求?
寻找适配器 HandlerAdapter
前面已经找到了对应的处理器了,下一步就得找到它对应的适配器
DispatcherServlet#getHandlerAdapter
protected getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } }
同样,HandlerAdapter
之间也有优先级概念,由于第 0 位是 RequestMappingHandlerAdapter
,而它的 supports
方法总是返回 true
,所以毫无疑问返回了它
......