最近抽时间看了一部分springmvc的源码,先写一篇文章总结一下。
众所周知springmvc是运行在spring父容器中的子容器,在web.xml中通过如下配置来让tomcat加载。
以前一直不知道为什么父容器会比子容器先加载,研究了一下tomcat的原理后才知道tomcat的加载顺序是context-param >> listener >> filter >> servlet。
所以在ContextLoaderListener和DispatcherServlet中都打上断点,调试时会看到ContextLoaderListener中相关初始化方法会先被执行。
(一)spring IOC容器
先大致看下spring父容器初始化的时候干了些什么。
ContextLoaderListener继承了ContextLoader,并实现了ServletContextListener接口中的contextInitialized方法。
web容器初始化时调用contextInitialized方法通知listener,继而调用ContextLoader中的initWebApplicationContext方法,初始化spring容器。
在这个init方法中,主要做了两件事:
1)创建容器实例。
2)设置初始化配置,读取配置文件、重启IOC容器(BeanFactory)加载各个资源文件和bean等。然后,将ServletContext中的spring根容器设置为本容器实例。
此时,spring父容器中的bean都已准备就绪。
(二)spring mvc初始化
SpringMVC的核心分发器DispatcherServlet继承自HttpServletBean,HttpServletBean重写了Servlet的init方法。
因为配置了<load-on-startup>,tomcat在启动时就会装载并创建DispatcherServlet的实例对象,并调用此servlet实例对象的init()方法。
在这个init方法中,主要做了三件事:
1)使用ServletConfig对象获取web.xml中此servlet配置的init-param参数,并设置到ServletConfigPropertyValues中。然后使用BeanWrapper构造DispatcherServlet,利用Spring的注入特性,调用setPropertyValues方法将web.xml中配置的属性设置到对应实例中,也就是以依赖注入的方式初始化属性。
2)调用initServletBean方法。此方法由子类FrameworkServlet重写,对webApplicationContext进行了初始化。
initWebApplicationContext方法创建了webApplicationContext,并将spring根上下文设置为父上下文,然后配置运行环境、ServletContext、ServletConfig等实例到这个上下文中。最后将新创建的容器上下文设置到ServletContext中。
ApplicationContext允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上。
3)随后由监听器触发onRefresh方法,此方法由DispatcherServlet重写,初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。
此时,springmvc也已准备就绪。