@[toc]
SpringMVC 九大组件,后面和大家剖析了 HandlerMapping,明天咱们来看 HandlerAdapter。因为后面曾经做了很多铺垫了,所以明天的内容不会太难。

SpringMVC 中通过 HandlerAdapter 来让 Handler 失去执行,为什么拿到 Handler 之后不间接执行呢?那是因为 SpringMVC 中咱们定义 Handler 的形式多种多样(尽管日常开发中咱们都是应用注解来定义,然而实际上还有其余形式),不同的 Handler 当然对应不同的执行形式,所以这两头就须要一个适配器 HandlerAdapter。

1.HandlerAdapter 体系

咱们先来看看 HandlerAdapter 大抵的继承关系:

能够看到,这里波及到的子类并不多,而且看名字基本上就晓得这个是干啥的。

这里除了 RequestMappingHandlerAdapter 比较复杂之外,其余几个都是比拟容易的,容易是因为这几个调用的办法都是固定的,简单则是因为调用的办法不固定。咱们先来看看这几个容易的。

2.HandlerAdapter

HttpRequestHandlerAdapter、SimpleServletHandlerAdapter、HandlerFunctionAdapter、SimpleControllerHandlerAdapter,这四个里边,HandlerFunctionAdapter 用来解决 HandlerFunction 类型的接口,函数式 Web 框架当初呼声很高,然而理论利用的并不多,因而这里咱们暂且疏忽它,来看另外三个。

HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 次要用来解决实现了 HttpRequestHandler 接口的 handler,例如像上面这种:

public class HelloController01 implements HttpRequestHandler {    @Override    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        response.getWriter().write("hello");    }}

HttpRequestHandlerAdapter#handle 办法也很简略,间接调用 handleRequest:

public class HttpRequestHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof HttpRequestHandler);    }    @Override    @Nullable    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        ((HttpRequestHandler) handler).handleRequest(request, response);        return null;    }    @Override    public long getLastModified(HttpServletRequest request, Object handler) {        if (handler instanceof LastModified) {            return ((LastModified) handler).getLastModified(request);        }        return -1L;    }}

能够看到,supports 办法判断 handler 的类型是否是 HttpRequestHandler,handle 办法则间接调用 handleRequest。

SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 次要用来解决实现了 Controller 接口的 handler,像上面这样:

public class HelloController02 implements Controller {    @Override    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {        return new ModelAndView("hello");    }}

SimpleControllerHandlerAdapter 的定义也很简略:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof Controller);    }    @Override    @Nullable    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return ((Controller) handler).handleRequest(request, response);    }    @Override    public long getLastModified(HttpServletRequest request, Object handler) {        if (handler instanceof LastModified) {            return ((LastModified) handler).getLastModified(request);        }        return -1L;    }}

这个应该不必再解释了吧。

SimpleServletHandlerAdapter

这个用来解决实现了 Servlet 接口的 handler,在理论开发中咱们很少用到这种,我就不多说,咱们间接来看它的源码:

public class SimpleServletHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof Servlet);    }    @Override    @Nullable    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        ((Servlet) handler).service(request, response);        return null;    }    @Override    public long getLastModified(HttpServletRequest request, Object handler) {        return -1;    }}

能够看到,也很简略!handle 办法中间接调用 servlet 的 service 办法。

能够看到,这三种 HandlerAdapter 简略的起因次要是因为要调用的办法比较简单,间接调用就能够了。而 RequestMappingHandlerAdapter 简单是因为调用的办法名不固定,所以简单。

3.RequestMappingHandlerAdapter

RequestMappingHandlerAdapter 继承自 AbstractHandlerMethodAdapter,咱们先来看看 AbstractHandlerMethodAdapter。

3.1 AbstractHandlerMethodAdapter

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {    private int order = Ordered.LOWEST_PRECEDENCE;    public AbstractHandlerMethodAdapter() {        super(false);    }    public void setOrder(int order) {        this.order = order;    }    @Override    public int getOrder() {        return this.order;    }    @Override    public final boolean supports(Object handler) {        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));    }    protected abstract boolean supportsInternal(HandlerMethod handlerMethod);    @Override    @Nullable    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return handleInternal(request, response, (HandlerMethod) handler);    }    @Nullable    protected abstract ModelAndView handleInternal(HttpServletRequest request,            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;    @Override    public final long getLastModified(HttpServletRequest request, Object handler) {        return getLastModifiedInternal(request, (HandlerMethod) handler);    }    protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);}

AbstractHandlerMethodAdapter 还是比较简单的,后面咱们说的三个办法还是没变,只不过三个办法里边别离调用了 supportsInternal、handleInternal 以及 getLastModifiedInternal,而这些 xxxInternal 都将在子类 RequestMappingHandlerAdapter 中被实现。另外 AbstractHandlerMethodAdapter 实现了 Ordered 接口,意味着在配置的时候还能够设置优先级。

这就是 AbstractHandlerMethodAdapter,比较简单。

3.2 RequestMappingHandlerAdapter

RequestMappingHandlerAdapter 承当了大部分的执行工作,通过后面的介绍,咱们曾经大抵晓得 RequestMappingHandlerAdapter 中次要实现了三个办法:supportsInternal、handleInternal 以及 getLastModifiedInternal。其中 supportsInternal 总是返回 true,意味着在 RequestMappingHandlerAdapter 中不做任何比拟,getLastModifiedInternal 则间接返回 -1,所以对它来说,最重要的其实是 handleInternal 办法。

整体上来说,RequestMappingHandlerAdapter 要解决三方面的问题:

  1. 解决申请参数。
  2. 调用处理器执行申请。
  3. 解决申请响应。

三个问题第一个最简单!大家想想咱们平时定义接口时,接口参数是不固定的,有几个参数、参数类型是什么都不确定,有的时候咱们还利用 @ControllerAdvice 注解标记了一些全局参数,等等这些都要思考进来,所以这一步是最简单的,剩下的两步就比拟容易了。对于参数的解决,SpringMVC 中提供了很多参数解析器,在接下来的源码剖析中,咱们将一步一步见识到这些参数解析器。

3.2.1 初始化过程

那么接下来咱们就先来看 RequestMappingHandlerAdapter 的初始化过程,因为它实现了 InitializingBean 接口,因而 afterPropertiesSet 办法会被主动调用,在 afterPropertiesSet 办法中,实现了各种参数解析器的初始化:

@Overridepublic void afterPropertiesSet() {    // Do this first, it may add ResponseBody advice beans    initControllerAdviceCache();    if (this.argumentResolvers == null) {        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);    }    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);    }}private void initControllerAdviceCache() {    if (getApplicationContext() == null) {        return;    }    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();    for (ControllerAdviceBean adviceBean : adviceBeans) {        Class<?> beanType = adviceBean.getBeanType();        if (beanType == null) {            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);        }        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);        if (!attrMethods.isEmpty()) {            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);        }        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);        if (!binderMethods.isEmpty()) {            this.initBinderAdviceCache.put(adviceBean, binderMethods);        }        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {            requestResponseBodyAdviceBeans.add(adviceBean);        }    }    if (!requestResponseBodyAdviceBeans.isEmpty()) {        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);    }}

这里首先调用 initControllerAdviceCache 办法对蕴含了 @ControllerAdvice 注解的类进行解决(如果有小伙伴对 @ControllerAdvice 注解的应用还不相熟,能够在公众号【江南一点雨】后盾回复 666 或者 ssm,有松哥的入门教程,里边都有对于 @ControllerAdvice 的解说),具体的解决思路如下:

  1. 首先查找到所有标记了 @ControllerAdvice 注解的 Bean,将查找后果保留到 adviceBeans 变量中。
  2. 接下来遍历 adviceBeans,找到对象中蕴含 @ModelAttribute 注解的办法,将查找的后果保留到 modelAttributeAdviceCache 变量中。
  3. 找到对象中蕴含 @InitBinder 注解的办法,将查找的后果保留到 initBinderAdviceCache 变量中。
  4. 查找实现了 RequestBodyAdvice 或者 ResponseBodyAdvice 接口的类,并最终将查找后果增加到 requestResponseBodyAdvice 中。实现了 RequestBodyAdvice 或者 ResponseBodyAdvice 接口的类个别须要用 @ControllerAdvice 注解标记,对于这两个接口的具体用法,读者能够参考松哥之前的文章,传送门如何优雅的实现 Spring Boot 接口参数加密解密?。另外这里还须要留神,找到的 adviceBean 并没有间接放到全局变量中,而是先放在局部变量中,而后才增加到全局的 requestResponseBodyAdvice 中,这种形式能够确保 adviceBean 始终处于汇合的最后面。

这就是 initControllerAdviceCache 办法的解决逻辑,次要是解决了一些全局参数的解决问题。

咱们再回到 afterPropertiesSet 办法中,接下来就是对 argumentResolvers、initBinderArgumentResolvers 以及 returnValueHandlers 的初始化了。

前两个参数相干的解析器,都是通过 getDefaultXXX 办法获取的,并且把获取的后果增加到 HandlerMethodArgumentResolverComposite 中,这种 xxxComposite,大家一看就晓得是一个责任链模式,这个里边治理了诸多的参数解析器,然而它本人不干活,须要工作的时候,它就负责遍历它所治理的参数解析器,让那些参数解析器去解决参数问题。咱们先来看看它的 getDefaultXXX 办法:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);    // Annotation-based argument resolution    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));    resolvers.add(new RequestParamMapMethodArgumentResolver());    resolvers.add(new PathVariableMethodArgumentResolver());    resolvers.add(new PathVariableMapMethodArgumentResolver());    resolvers.add(new MatrixVariableMethodArgumentResolver());    resolvers.add(new MatrixVariableMapMethodArgumentResolver());    resolvers.add(new ServletModelAttributeMethodProcessor(false));    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));    resolvers.add(new RequestHeaderMapMethodArgumentResolver());    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));    resolvers.add(new SessionAttributeMethodArgumentResolver());    resolvers.add(new RequestAttributeMethodArgumentResolver());    // Type-based argument resolution    resolvers.add(new ServletRequestMethodArgumentResolver());    resolvers.add(new ServletResponseMethodArgumentResolver());    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));    resolvers.add(new RedirectAttributesMethodArgumentResolver());    resolvers.add(new ModelMethodProcessor());    resolvers.add(new MapMethodProcessor());    resolvers.add(new ErrorsMethodArgumentResolver());    resolvers.add(new SessionStatusMethodArgumentResolver());    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());    if (KotlinDetector.isKotlinPresent()) {        resolvers.add(new ContinuationHandlerMethodArgumentResolver());    }    // Custom arguments    if (getCustomArgumentResolvers() != null) {        resolvers.addAll(getCustomArgumentResolvers());    }    // Catch-all    resolvers.add(new PrincipalMethodArgumentResolver());    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));    resolvers.add(new ServletModelAttributeMethodProcessor(true));    return resolvers;}private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);    // Annotation-based argument resolution    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));    resolvers.add(new RequestParamMapMethodArgumentResolver());    resolvers.add(new PathVariableMethodArgumentResolver());    resolvers.add(new PathVariableMapMethodArgumentResolver());    resolvers.add(new MatrixVariableMethodArgumentResolver());    resolvers.add(new MatrixVariableMapMethodArgumentResolver());    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));    resolvers.add(new SessionAttributeMethodArgumentResolver());    resolvers.add(new RequestAttributeMethodArgumentResolver());    // Type-based argument resolution    resolvers.add(new ServletRequestMethodArgumentResolver());    resolvers.add(new ServletResponseMethodArgumentResolver());    // Custom arguments    if (getCustomArgumentResolvers() != null) {        resolvers.addAll(getCustomArgumentResolvers());    }    // Catch-all    resolvers.add(new PrincipalMethodArgumentResolver());    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));    return resolvers;}

这两个办法尽管挺长的,然而内容都比较简单。在 getDefaultArgumentResolvers 办法中,解析器被分为四类:

  1. 通过注解标记的参数对应的解析器。
  2. 通过类型解析的解析器。
  3. 自定义的解析器。
  4. 兜底的解析器(大部分后面搞不定的参数前面的都能搞定,简略数据类型就是在这里解决的)。

getDefaultInitBinderArgumentResolvers 办法也是相似的,我就不再赘述。

所谓的解析器实际上就是把参数从对应的介质中提取进去,而后交给办法对应的变量。如果我的项目有须要,也能够自定义参数解析器,自定义的参数解析器设置到 RequestMappingHandlerAdapter#customArgumentResolvers 属性上,在调用的时候,前两种参数解析器都匹配不上的时候,自定义的参数解析器才会有用,而且这个程序是固定的无奈扭转的。

对于参数解析器自身,如果大家不是特地相熟的话,能够看看松哥之前的文章:

  • SpringBoot 中如何自定义参数解析器?
  • 深入分析 SpringMVC 参数解析器

3.2.2 申请执行过程

依据后面的介绍,申请执行的入口办法实际上就是 handleInternal,所以这里咱们就从 handleInternal 办法开始剖析:

@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {    ModelAndView mav;    checkRequest(request);    // Execute invokeHandlerMethod in synchronized block if required.    if (this.synchronizeOnSession) {        HttpSession session = request.getSession(false);        if (session != null) {            Object mutex = WebUtils.getSessionMutex(session);            synchronized (mutex) {                mav = invokeHandlerMethod(request, response, handlerMethod);            }        }        else {            // No HttpSession available -> no mutex necessary            mav = invokeHandlerMethod(request, response, handlerMethod);        }    }    else {        // No synchronization on session demanded at all...        mav = invokeHandlerMethod(request, response, handlerMethod);    }    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);        }        else {            prepareResponse(response);        }    }    return mav;}

不过这个 handleInternal 办法看起来平平无奇,没啥特别之处。认真看起来,它里边就做了三件事:

  1. checkRequest 办法查看申请。
  2. invokeHandlerMethod 办法执行处理器办法,这个是外围。
  3. 解决缓存问题。

那么接下来咱们就对这三个问题别离进行剖析。

checkRequest
protected final void checkRequest(HttpServletRequest request) throws ServletException {    // Check whether we should support the request method.    String method = request.getMethod();    if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {        throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);    }    // Check whether a session is required.    if (this.requireSession && request.getSession(false) == null) {        throw new HttpSessionRequiredException("Pre-existing session required but none found");    }}

查看就查看了两个货色:

  1. 是否反对申请办法

当 supportedMethods 不为空的时候,去查看是否反对申请办法。默认状况下,supportedMethods 为 null,所以默认状况下是不查看申请办法的。如果须要查看,能够在注册 RequestMappingHandlerAdapter 的时候进行配置,如果在构造方法中设置 restrictDefaultSupportedMethods 变量为 true,那么默认状况下只反对 GET、POST、以及 HEAD 三种申请,不过这个参数改起来比拟麻烦,默认在父类 AbstractHandlerMethodAdapter 类中写死了。

  1. 是否须要 session

如果 requireSession 为 true,就检查一下 session 是否存在,默认状况下,requireSession 为 false,所以这里也是不查看的。

invokeHandlerMethod

接下来就是 invokeHandlerMethod 办法的调用了:

@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {    ServletWebRequest webRequest = new ServletWebRequest(request, response);    try {        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);        if (this.argumentResolvers != null) {            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);        }        if (this.returnValueHandlers != null) {            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);        }        invocableMethod.setDataBinderFactory(binderFactory);        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);        ModelAndViewContainer mavContainer = new ModelAndViewContainer();        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));        modelFactory.initModel(webRequest, mavContainer, invocableMethod);        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);        asyncWebRequest.setTimeout(this.asyncRequestTimeout);        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);        asyncManager.setTaskExecutor(this.taskExecutor);        asyncManager.setAsyncWebRequest(asyncWebRequest);        asyncManager.registerCallableInterceptors(this.callableInterceptors);        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);        if (asyncManager.hasConcurrentResult()) {            Object result = asyncManager.getConcurrentResult();            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];            asyncManager.clearConcurrentResult();            LogFormatUtils.traceDebug(logger, traceOn -> {                String formatted = LogFormatUtils.formatValue(result, !traceOn);                return "Resume with async result [" + formatted + "]";            });            invocableMethod = invocableMethod.wrapConcurrentResult(result);        }        invocableMethod.invokeAndHandle(webRequest, mavContainer);        if (asyncManager.isConcurrentHandlingStarted()) {            return null;        }        return getModelAndView(mavContainer, modelFactory, webRequest);    }    finally {        webRequest.requestCompleted();    }}

这个办法里波及到的组件比拟多,不过大部分组件在后面的文章中松哥都曾经和大家介绍过了,因而这里了解起来应该并不难。

  1. 首先获取一个 WebDataBinderFactory 对象,该对象将用来构建 WebDataBinder。
  2. 接下来获取一个 ModelFactory 对象,该对象用来初始化/更新 Model 对象(见:猜一猜,Model 中的数据放在 session 中还是 request 中?)。
  3. 接下来创立 ServletInvocableHandlerMethod 对象,一会办法的调用,将由它实现(见:Spring Boot 定义接口的办法是否能够申明为 private?)。
  4. 接下来给 invocableMethod 把须要的参数都安顿上。
  5. 结构一个 ModelAndViewContainer 对象,未来用来存储 Model 和 View。
  6. 把 FlashMap 中的数据先增加进 ModelAndViewContainer 容器中(对于 FlashMap,参见:SpringMVC 中的参数还能这么传递?涨姿态了!)。
  7. 接下来初始化 Model,解决 @SessionAttributes 注解和 WebDataBinder 定义的全局数据,同时配置是否在重定向时疏忽 defaultModel(具体初始化过程见:猜一猜,Model 中的数据放在 session 中还是 request 中?)。
  8. 接下来解决异步申请状况,判断是否有异步申请后果。
  9. 调用 invokeAndHandle 办法去真正执行接口办法,具体过程参见Spring Boot 定义接口的办法是否能够申明为 private?一文。
  10. 如果是异步申请,则间接返回即可。
  11. 接下来调用 getModelAndView 办法去结构 ModelAndView 并返回,在该办法中,首先会去更新 Model,更新的时候会去解决 SessionAttribute 同时配置 BindingResult;而后会依据 ModelAndViewContainer 去创立一个 ModelAndView 对象;最初,如果 ModelAndViewContainer 中的 Model 是 RedirectAttributes 类型,则将其设置到 FlashMap 中。
  12. 最初设置申请实现。
解决缓存

缓存的解决次要是针对响应头的 Cache-Control 字段。

这块比较简单,松哥就不废话了。

4.小结

明天这篇文章次要和小伙伴们聊了 SpringMVC 中的 HandlerAdapter,HandlerAdapter 中波及到了很多其余的组件,这些组件松哥在之前的文章中曾经向大家介绍过了,因而本文其实也能够看成是对后面文章所学习组件的一个梳理。当然这里波及到的技术细节还是十分多,松哥当前抽空录视频和大家细聊。

好啦,本文咱们就先说这么多~