第一步 加载默认策略
DispatcherServlet类加载的时候会执行其static代码块,其中读取其内部的DispatcherServlet.properties属性文件加载所有配置的属性,以key,value的形式加载到名为defaultStrategies的Properties中作为其默认的策略。
上面的DEFAULT_STRATEGIES_PATH就是文件名DispatcherServlet.properties,看下这个属性文件包含了哪些:
其中比较常用的:
两个HandlerMapping:
1.BeanNameUrlHandlerMapping
Request到Controller的映射 request-bean 比如"/test"的请求会映射到加上@Component("/test")这个注解并且实现了Controller接口的bean 其底层存了一个key为request,value为bean的map
2.RequestMappingHandlerMapping
Request到Controller的映射 request-method 就是我们常用的@RequestMapping注解,请求会被映射到具体的方法 其底层存了一个key为request,value为method的map
三个HandlerAdapter:
1.RequestMappingHandlerAdapter
主要是适配注解类处理器,注解类处理器就是我们经常使用的@Controller的这类处理器,其实就是对应上面的RequestMappingHandlerMapping
2.SimpleControllerHandlerAdapter
适配实现了Controller接口或Controller接口子类的处理器,其实就是对应上面的BeanNameUrlHandlerMapping
3.HttpRequestHandlerAdapter
主要是适配静态资源处理器,静态资源处理器就是实现了HttpRequestHandler接口的处理器,这类处理器的作用是处理通过SpringMVC来访问的静态资源的请求。
一个ViewResolver:
InternalResourceViewResolver(到具体页面的视图解析器)
第二步 初始化DispatcherServlet
加载完配置之后,就是初始化了。当web容器启动的时候,Servlet会执行初始化,具体的调用链路是
HttpServletBean#init方法 ->
FrameworkServlet#initServletBean ->
DispatcherServlet#onRefresh ->
DispatcherServlet#initStrategies
initMultipartResolver(context)用来初始化上传文件的bean,里面是固定使用multipartResolver这个名字去spring容器中获取bean的,所以这里也可以看出spring为什么规定上传文件的bean名字必须是multipartResolver。
后面开始初始化各种从DispatcherServlet.properties中加载到的组件,这里以initHandlerMappings为例:
所以如果我们自定义一个handlerMapping通过bean的形式注入的话,spring就会抛弃掉默认的了!
第三步请求进来
其实第二步之后springmvc的初始化步骤就完成了,之后请求通过HttpServlet的doGet或者doPost,通过调用链最终调到DispatcherServlet的doDispatch方法。
doDispatch方法主要做了这么几件事情:
第一件处理文件上传
第二件通过请求匹配对应的处理器映射器HandlerMapping(就是上面讲的两个)
具体代码在getHandler方法中:
这里首先遍历所有的handlerMappings,找到符合的handlerMapping,我们还是已RequestMappingHandlerMapping为例,看下他的getHandler方法,这个方法其实是其父类AbstractHandlerMapping的:
其实AbstractHandlerMapping基本上是所有handlerMapping的父类,最终返回的都是一个HandlerExecutionChain对象,
不同的就是这个getHandlerInternal方法,它其实是AbstractHandlerMapping的一个抽象方法,须要子类实现的,可以看到spring将其返回值定义为了object,最终这个handler会被包装到HandlerExecutionChain对象中(其中还会包含几个拦截器对象)。
也就是说每个handlerMapping返回的handler可能都不一样!RequestMappingHandlerMapping返回的就是一个HandlerMethod对象(意味着请求到方法的映射,也符合@RequestMapping注解的使用方式),BeanNameUrlHandlerMapping返回的就是一个Object对象(其实就是对应一个Controller对象,也符合请求到bean的映射关系)
这里还是说RequestMappingHandlerMapping的getHandlerInternal方法:
主要是利用了lookupHandlerMethod方法找到了这个映射,这个方法中主要是利用了一个MappingRegistry对象获取到映射:
MappingRegistry中就定义了许多的map存储这些映射关系,
那么这些映射关系是什么时候维护起来的呢?事实上是RequestMappingHandlerMapping实现了InitializingBean接口,当其实例化之后会调用afterPropertiesSet方法,意思就是当RequestMappingHandlerMapping初始化时扫描出这些映射关系出来,具体的调用链路:
AbstractHandlerMethodMapping#afterPropertiesSet方法 ->
AbstractHandlerMethodMapping#initHandlerMethods方法 ->
AbstractHandlerMethodMapping#detectHandlerMethods方法 ->
AbstractHandlerMethodMapping#registerHandlerMethod方法 ->
MappingRegistry#register方法。
其中在initHandlerMethods方法中Spring会获取所有的beanNames,然后拿到其类型,判断这个类型是否符合扫描标准:
可以看到加了@Controller或者@RequestMapping的bean会被扫描!
第三件通过HandlerExecutionChain的getHandler方法匹配到对应的处理器适配器HandlerAdapter
具体代码在getHandlerAdapter方法中:
对应的RequestMappingHandlerAdapter的supports方法:
因为RequestMappingHandlerMapping返回的是一个HandlerMethod对象,所以这里RequestMappingHandlerAdapter将被匹配到并返回出来。
第四件通过HandlerAdapter处理请求
处理请求主要下面几步:
我们关键来看下处理请求的方法,其调用链是:
RequestMappingHandlerAdapter#handle方法 ->
RequestMappingHandlerAdapter#handleInternal方法 ->
RequestMappingHandlerAdapter#invokeHandlerMethod方法
invokeHandlerMethod方法中实例化了一个ServletInvocableHandlerMethod对象,并且对这个对象设置了两个比较重要的属性argumentResolvers和returnValueHandlers:
这两个参数的初始化同样是RequestMappingHandlerAdapter利用了InitializingBean接口
argumentResolvers用来将请求中的参数绑定到方法的参数中。
returnValueHandlers则能够根据方法执行的返回值来确定如何解析视图。
后面执行这个ServletInvocableHandlerMethod对象的invokeAndHandle方法,
point1:
执行invokeForRequest方法其实就是执行我们真正的方法:
point2:
设置ResponseStatus,对应的是@ResponseStatus注解,可以指定响应的响应码和异常的文案
point3&point4:
如果有响应码或者响应异常,设置requestHandled属性为true
point5:
returnValueHandlers.handleReturnValue就是匹配到HandlerMethodReturnValueHandler来处理我们的响应。
这里已两个常用的HandlerMethodReturnValueHandler为例:
RequestResponseBodyMethodProcessor
可以看到RequestResponseBodyMethodProcessor可以处理加了@ResponseBody注解的方法返回值。
writeWithMessageConverters中有一个关键的代码,会遍历所有的HttpMessageConverters,找到匹配的然后写出去,最终调用的是HttpServletResponse#write方法。
ModelAndViewMethodReturnValueHandler
可以看到ModelAndViewMethodReturnValueHandler可以处理返回值类型是ModelAndView的方法,看下他的handleReturnValue方法:
可以看到方法内部其实就是先将返回值强转为ModelAndView,然后将ModelAndView中的相关参数赋值给了ModelAndViewContainer对象,这个对象是在执行RequestMappingHandlerAdapter#invokeHandlerMethod方法的时候new出来的,并且在执行ServletInvocableHandlerMethod#invokeAndHandle作为参数传了进去,最终当invokeAndHandle方法执行完毕之后,又将ModelAndViewContainer对象转成了ModelAndView返回。
这里有个地方可以看到如果requestHandled是true的时候,就直接返回null,这个场景就是上面说的当有响应码或者响应异常时会设为true,另外当响应处理器是RequestResponseBodyMethodProcessor时也会设为true,这种场景直接输出json或者其他文本,也不需要返回视图。