接上一遍博客《SpringMVC源码深度解析(上)》继续聊。最后聊到了SpringMVC的九大组建的初始化,以 HandlerMapping为例,SpringMVC提供了三个实现了,分别是:BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping,其中最常用的就是 RequestMappingHandlerMapping,与@RequestMapping注解相关,以它为例,先看看类的继承图:
可知该类实现了InitializingBean接口,这是Spring的扩展点,Bean对象在初始化的时候,会调用 RequestMappingHandlerMapping#afterPropertiesSet()方法。如果使用@EnableWebMvc注解或者手动往Spring容器中注入 RequestMappingHandlerMapping对象,Web容器在调用refresh()方法的过程中,会调用afterPropertiesSet()方法,如果不是通过以上两种方式的话,由上一篇博客 《SpringMVC源码深度解析(上)》可知,会读取dispatcherservlet.properties文件,获取默认的HandlerMapping的实现类,但是由于在前面已经调用了Web容器的refresh()方法,完成了容器的刷新,因此,这里需要特殊处理,代码如下:
可知,这里会显示的调用AutowireCapableBeanFactory#createBean()方法,才会经历Bean的实例化与初始化,才会调用 InitializingBean#afterPropertiesSet()方法。我专门提到这个方法,也侧面说明这个方法很重要,看看这个方法,代码如下:
重点看看AbstractHandlerMethodMapping#getMappingForMethod()方法,代码如下:
再看看AbstractHandlerMethodMapping#registerHandlerMethod()方法,代码如下:
到这里为止就可以知道:RequestMappingHandlerMapping在初始化的时候,就会获取到所有被@Controller/@RequestMapping注解修饰的类,并且得到该类中所有被@Requestmapping注解修饰的方法,每个被@RequestMapping注解修饰的方法,最终生成一个对应的HandlerMethod对象(存储被@Controller/@RequestMapping注解修饰的对象和被@RequestMapping注解修饰的方法),并存储在内部类RequestMappingHandlerMapping.MappingRegistry的registry(Map)属性中,key为RequestMappingInfo对象,value为MappingRegistration对象(存储HandlerMethod对象)。
聊完了RequestMappingHandlerMapping在初始化,再聊聊SpringMVC是如何处理请求。其实就是要搞清楚DispatcherServlet的原理,它是一个Servlet,在《SpringMVC源码深度解析(上)》中也说到了,创建了DispatcherServlet对象后,会添加到Servlet容器中。如果有请求来了,会解析url并匹配,如果是符合DispatcherServlet的匹配路径,则调用 GenericServlet#service()方法,这是一个抽象方法,因此实际调用的是HttpServlet#service()方法,代码如下:
以POST请求为例,则调用的是HttpServlet#doPost()方法,代码如下:
其实对于DispatcherServlet来讲,不管是哪种请求,最终都是调用FrameworkServlet#processRequest()方法,代码如下:
DispatcherServlet#doDispatch()方法是处理请求的核心,会重点讲,代码如下:
在AbstractHandlerMethodMapping#lookupHandlerMethod()方法中,主要是进行路径的匹配,由于在前面RequestMappingInfoHandlerMapping初始化的时候,已经存储好了所有的匹配路径及其HandlerMethod,存放在 RequestMappingInfoHandlerMapping.MappingRegistry的pathLookup属性中,因此如果满足匹配要求,是可以获取到HandlerMethod对象的,这块就说了,涉及到匹配逻辑,比较复杂,感兴趣可以自己研究。回到AbstractHandlerMapping#getHandler()方法中,看看AbstractHandlerMapping#getHandlerExecutionChain()方法,代码如下:
可知这这个方法中,就是创建HandlerExecutionChain对象,并将HandlerInterceptor设置到HandlerExecutionChain对象的interceptorList属性中。再回到DispatcherServlet#doDispatch()方法中,代码如下:
因此返回的HandlerAdapter对象是RequestMappingHandlerAdapter,这里当用到的是适配器设计模式,为什么要做适配呢?还记得之前HandlerMapping由三个实现类的,实际上处理的是三种不同的Controller,因此需要有对应的适配器处理不同的情况,看看HandlerAdapter#handle()方法就知道了,具体如下:
主要是处理这三种情况:
① 一种是被@Controller/@RestController/@RequestMapping注解修饰的类;
② 实现了HttpRequestHandler接口的类;
③ 实现了Controller接口的类;
这三种情况的类,都属于Controller,因此需要不同的适配器处理,我主要讲①,剩下的两种,有兴趣可以试试。回到DispatcherServlet#doDispatch()方法继续往下看,代码如下:
执行拦截器的前置方法,就是遍历HandlerExecutionChain的interceptorList属性,执行HandlerInterceptor#preHandle()方法,代码如下:
执行拦截器的后置方法,也是如此,代码如下:
当然,如果执行某个拦截器的前置方法,返回的是 false,则直接return,不往下走了。在执行拦截器的前置方法后,后置方法前,会执行HandlerAdapter#handle()方法,代码如下:
顺便说一下,RequestMappingHandlerAdapter也实现了InitializingBean接口,因此也会实现afterPropertiesSet()方法,代码如下:
这里设置的这些默认处理器,后续会用到,包括参数的解析、绑定、返回值的处理等。
再看看 RequestMappingHandlerAdapter#invokeAndHandle()方法,这里是执行目标类中目标方法的关键,代码如下:
以PathVariableMethodArgumentResolver为例看看,代码如下:
回到InvocableHandlerMethod#getMethodArgumentValues()方法,看看是如何解析参数的,主要看:HandlerMethodArgumentResolverComposite#resolveArgument()方法,代码如下:
还是以PathVariableMethodArgumentResolver,看看它的resolveArgument()方法,代码如下:
POST请求为:http://127.0.0.1:9000/hello/sayHello/张三
最后将解析获取到的参数值,放入Request的Attribute中。当然,除了这个之外,常用的是在请求体中会放入Json数据,最后参数中接受的是一个对象,SpringMVC是怎么对Json格式的数据进行处理的呢?这里是通过RequestResponseBodyMethodProcessor处理的,代码如下:
一共有八个转换器,这是在哪里添加的呢?后面讲@EnableWebMvc注解的时候会讲到,再看看HttpMessageConverter#read()方法,代码如下:
到这里可以知道,接收的Java对象,必须有Setter方法,因为这里是直接通过反射调用Setter方法进行赋值的。到这个为止,完成了Json的解析以及对象的赋值,即下图:
还有一些其他的注解,比如@RequestParam,感兴趣的自己研究。InvocableHandlerMethod#invokeForRequest(),参数处理完了,再调用InvocableHandlerMethod#doInvoke()方法,代码如下:
反射调用玩目标方法后,会有返回值,也需要处理返回值。回到ServletInvocableHandlerMethod#invokeAndHandle()方法,代码如下:
由于返回的是对象,因此需要处理,由于HelloController上添加的是@RestController,这个注解相当于@Controller+@ResponseBody,因此最终讲返回的对象反序列化为Json格式的数据,并返回给前端。这里还是需要看RequestResponseBodyMethodProcessor#handleReturnValue()方法,代码如下:
该方法较长,我只截取重点讲。
到这里为止,把返回的对象进行序列化,只是将其转成字符并缓存起来,回到ObjectWriter#writeValue()方法,代码如下:
到这里为止,SpringMVC的返回值处理讲完了,SpringMVC剩下的内容将在下一篇博文中讲,敬请期待~