享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(上)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(上)

回顾


上期说道,HandlerAdapter存在的意义,就是充当request,reponse与我们定义的各种形式handler之间的参数适配,返回值适配。

当我们在享受着SpringMVC带来的多样化参数接收形式,以及简便的返回值操作时,殊不知,HandlerAdapter在背后默默的为我们奉献着。


RequestMappingHandlerAdapter


适用于@RequestMapping注解标注的Handler,是处理我们定义的controller接口最重要的HandlerAdapter。基于他的重要地位,本文讲讲他是如何工作的。


1.初始化

先从其初始化开始下手

为了应对handler的各种样式的参数接收,在handlerApater里,就需要大量的转换工具来类处理这些东西。

所以:初始化的过程中,就是要设置各种参数工具,返回值处理工具等等,这些工具可以是Spring 提供的或者开发人员自己定义的。

(1)
public RequestMappingHandlerAdapter() {
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
        stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316
        this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(stringHttpMessageConverter);
        this.messageConverters.add(new SourceHttpMessageConverter<Source>());
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
public void afterPropertiesSet() {
        (2)
        initControllerAdviceCache();
        (3)
        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);
        }
    }
1.1 设置HttpMessageConverter

HttpMessageConverter负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息


1.2 initControllerAdviceCache

这一步目的是对 @ControllerAdvice标注的Bean的处理

这里简单介绍下@ControllerAdvice注解意义,通过@ControllerAdvice标注的类一般作为全局异常处理,全局数据绑定,全局数据预处理等。

例如:我们在一个Controller里抛出一个自定义异常时,通常都会有一个@ControllerAdvice定义的类中处理此异常。

也就是说:@ControllerAdvice标注类 后,他已经成为一种处理工具。自然的SpringMVC在初始化时会把这些 开发人员定义的处理工具找到 缓存起来。

源码中initControllerAdviceCache()的逻辑是

  • 找到该Advice Bean内所有的标注有@ModelAttribute但没标注@RequestMapping的缓存到一起
  • 找到该Advice Bean内所有的标注有@InitBinder的方法缓存在一起
  • 实现了接口RequestBodyAdvice/ResponseBodyAdvice们,缓存到requestResponseBodyAdvice集合的前面


1.3 设置默认参数解析工具

getDefaultArgumentResolvers()方法中设置了大量的SpringMVC已经准备好的参数解析工具。为handlerApater处理做准备。

列如:我们常用@PathVariable注解。其实就是在此处设置的PathVariableMethodArgumentResolver解析工具帮我们解析的。

@PostMapping("/post/{id}/{name}")
    public void Post(@PathVariable(value = "id") String id,@PathVariable(value = "name") String name, HttpServletRequest request, HttpServletResponse response){
    }

工具设置逻辑为:

  • 先设置针对注解的参数解析工具
  • 基于type类型的解析工具设置
  • 设置用户定义的参数解析工具
  • 最终解析工具,如果上边都没解析,此处设置解析工具最终处理

这些参数解析工具统一交给HandlerMethodArgumentResolverComposite。从他的名字也可以看出解析工具混合器,说白了就是解析工具集合

这里提一下自己的一点小理解: 我在很多框架中都见过类型xxxComposite混合器,本质上就是同一个东西的集合。

既然有了集合为啥还要多一层出来呢?

个人觉得混合器就好比工头,当我们跟工人打交道时,直接与工头交涉,比跟多个工人交涉方便的多。

总结为:分工明确,高内聚低耦合


1.4 设置返回值处理工具

提供对HandlerMethod返回值的支持,比如@ResponseBody等同参数解析器逻辑差不多,

最终初始化HandlerMethodReturnValueHandlerComposite混合器,承载返回值处理器集合


1.5 其他

除了上述初始化外,当然还有其他细节,感兴趣可以去阅读源码


2.handle处理

一切准备就绪后,下面就是调用了

再回头看下DispatcherServlet中,HandlerAdapter的调用起点

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
获取当前hangdler的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
走过拦截器的前置处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
}
通过适配器执行调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
过拦截器后置处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}

RequestMappingHandlerAdapterhandle方法在父类中AbstractHandlerMethodAdapter回调RequestMappingHandlerAdapter.handleInternal方法


2.1 handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        checkRequest(request);
        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
            ...
        }
        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;
    }

这里只要是

  • request请求方法的检查,
  • 同一个Session下是否要串行(同步)
  • 调用invokeHandlerMethod
  • 处理Cache-Control这个请求头


2.1 invokeHandlerMethod

执行handler方法,好戏正式登场

protected 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);
            //对handlerMethod进行封装。
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//设置参数处理器
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        //设置返回值处理器
            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();
                if (logger.isDebugEnabled()) {
                    logger.debug("Found concurrent result value [" + result + "]");
                }
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
            //调用
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }


相关文章
|
6月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
75 0
|
3月前
|
前端开发 Java Spring
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
这篇文章通过示例代码展示了如何在Spring MVC中编写和注册拦截器,以及如何在拦截器的不同阶段添加业务逻辑。
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
|
存储 前端开发 搜索推荐
(八)Spring源码解析:Spring MVC
(八)Spring源码解析:Spring MVC
94 1
|
6月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
65 0
|
6月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
103 0
|
6月前
|
设计模式 前端开发 Java
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
|
前端开发 Java Spring
源码浅析SpringMVC请求的流转过程
Spring MVC框架使用了其”模型-视图-控制器”( Model-View-Controller )架构方式,用于开发灵活且松散耦合的 Web 应用程序。我们都使用过SpringMVC来处理信息,并渲染视图到Browser。但需要注意的是,在现在的架构中,大都采用了前后端分离的情况,而我们在使用SpringMVC的时候,只需要关注M(Model),C(Controller)这两个部分,而视图渲染的部分则交给了前端。
321 0
源码浅析SpringMVC请求的流转过程
|
Java Spring 容器
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
|
Java Spring
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
|
JSON 缓存 Java
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)