SpringMvc源码启动分析
SpringMvc源码启动分析(只分析针对老spring【web.xml】形式)—
在Spring的核心配置文件web.xml中,配置了ContextLoaderListener监听器
1 | <listener> |
contextLoaderListener实现了ServletContextListener接口
当一个Web应用部署到容器内时(eg.tomcat),在Web应用开始响应执行用户请求前,以下步骤会被依次执行:
- 部署描述文件中(eg.tomcat的web.xml)由
元素标记的事件监听器会被创建和初始化对于所有事件监听器,如果实现了ServletContextListener接口,将会执行其实现的contextInitialized()方法 - 部署描述文件中由<filter>元素标记的过滤器会被创建和初始化,并调用其init()方法
- 部署描述文件中由<servlet>元素标记的servlet会根据
的权值按顺序创建和初始化,并调用其init()方法
springmvc启动的流程图大概如下:
常见的web.xml如下:
1 | <web-app> |
Listener的初始化过程
首先定义了:<context-param>标签,用于配置一个全局变量,<context-param>标签的内容读取后会被放进application中,做为Web应用的全局变量使用,接下来创建listener时会使用到这个全局变量,因此,Web应用在容器中部署后,进行初始化时会先读取这个全局变量,之后再进行上述讲解的初始化启动过程。
ContextLoaderListener的contextInitialized()方法:
进入initWebApplicationContext()方法:
这里的ConfigurableWebApplicationContext实现类是XmlWebApplicationContext(xml解析web上下文)
进入configureAndRefreshWebApplicationContext()方法:
进入wac.refresh()方法:
进入具体的实现类发现是AbstractApplicationContext类,springboot启动的时候refreshContext()方法也是调用的这里的refresh()方法来解析加载ioc容器和自动配置。(注意:springBoot中的war包启动并没有用到contextLoaderListener,仅限老spring)
接下来进入DispatcherServlet的初始化:接下来进入DispatcherServlet的初始化:
DispatcherServlet→FrameWorkServlet→HttpServletBean
进入HttpServletBean的init()方法:
进入FrameServlet的initServletBean()方法:
创建WebApplicationContext:
ApplicationContxt被实例化之后,做一些配置,设置当前的Environment,设置父类(如果有的话),获取contextLocation的位置(web.xml中可以设置,当然也有默认值)
回到DispatcherServlet的onfresh()方法
这里就是初始化springmvc的一些组件:resolver,handlermapping,handlerAdapter等等
以下文章参考自:https://www.cnblogs.com/sw008/p/11054263.html
着重分析注册HandlerMapping:
整体思路
Spring Mvc通过HandlerMapping返回执行链。在Spring容器中有多中不同的HandlerMapping实现,其对应不同的映射配置方式。在使用@RequestMapping注解时,SpringMvc通过RequestMappingHandlerMapping类的Bean解析、注册、缓存映射关系,并提供匹配执行链的功能。
RequestMappingHandlerMapping
1 解析 url-method 映射关系 思路
RequestMappingHandlerMapping实现了InitializingBean接口,在其初始化时执行afterPropertiesSet方法。在此方法中其遍历ApplicationContext中所有Bean,通过反射判断其类型Class上是否有@Controller或@RequestMapping注解。
若Class上有此类注解说明这个Bean是Controller。则执行detectHandlerMethods(beanName)方法,反射(clazz.getMethods())并遍历此Bean的Method[],通过反射method.getAnnotation(RequestMapping)判断并获取方法上标注的@RequestMapping配置信息。将@RequestMapping信息封装成RequestMappingInfo,将此method封装成HandlerMethod。注册到RequestMappingHandlerMapping的mappingRegistry中。
2 注册并缓存 url-method 映射关系
RequestMappingHandlerMapping中的mappingRegistry对象中成员变量:各种Map还有一个读写锁。
mappingRegistry的各个主要变量
3 接收请求在RequestMappingHandlerMapping中查找返回映射
通过request的请求URL从urlLookup中找到对应RequestMappingInfo,再通过RequestMappingInfo在mappingLookup中找到HandlerMethod。HandlerMethod为url对应的@RequestMapping标注执行方法。
解析注册具体实现
WebConfig上使用了@EnableWebMvc注解,这个注解导入了DelegatingWebMvcConfiguration配置,在DelegatingWebMvcConfiguration的父类WebMvcConfigurationSupport创建了RequestMappingHandlerMapping。
1 |
|
RequestMappingHandlerMapping加载
@RequestMapping
我一开始也不知道创建RequestMappingHandlerMapping作用,后来看到它的类图就明白加载过程。
RequestMappingHandlerMapping的父类实现了InitializingBean,那么在初始化的时候就会调用afterPropertiesSet方法。看下父类afterPropertiesSet的实现:
1 |
|
afterPropertiesSet实现交给了initHandlerMethods,initHandlerMethods的执行流程如下:
- 得到所有的beanName
- 遍历beanNames,调用isHandler判断bean是不是一个controller,isHandler由子类实现,RequestMappingHandlerMapping的实现如下,判断bean中有没有Controller或RequestMapping
1 |
|
1.查找controller中有@RequestMapping注解的方法,并注册到请求容器中
1 | protected void detectHandlerMethods(final Object handler) { |
detectHandlerMethods是加载请求核心方法,执行流程如下:
- 得到controller真实类型,controller可能被代理
- 根据方法上的@RequestMapping信息构建RequestMappingInfo,由RequestMappingHandlerMapping实现。从代码上可以看出,必须方法上声明@RequestMapping,类上的@RequestMapping才会生效。
1 |
|
- 封装所有的Method和RequestMappingInfo到Map中
- 将RequestMappingInfo注册到请求容器中这里就不再分析register方法的实现过程,主要是根据handler,method封装成HandlerMethod,再请求的时候会得到HandlerMethod,然后反射调用。
1
2
3protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}