Web应用的初始化总体过程
当一个Web应用部署到容器(tomcat)内时,在Web应用开始响应执行用户请求前,以下步骤会被依次执行:
- 部署描述文件(所有web应用启动的初始位置都在web.xml)中,由
<listener>
元素标记的事件监听器会被创建和初始化。 - 对于所有事件监听器,如果实现了
ServletContextListener
接口,将会执行其实现的contextInitialized()
方法。(Servlet API提供了监听器,Servlet和Filter的初始化前和销毁后会给实现了ServletContextListener
接口的监听器发送通知,采用观察者模式,也称为为订阅-发布模式) - 部署描述文件中由
<filter>
元素标记的过滤器会被创建和初始化,并调用其init()
方法。 - 部署描述文件中由
<servlet>
元素标记的servlet会根据<load-on-startup>
的权值按顺序创建和初始化,并调用其init()
方法。
流程图:
对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
Spring根IOC容器初始化
web容器启动,web.xml中由<listener>
元素标记的事件监听器被创建和初始化。contextInitialized()方法被执行,调用父类initWebApplicationContext。
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>
1 通过反射创建根IOC容器:XmlWebApplicationContext。
2 调用configureAndRefreshWebApplicationContext:
1)设置全局上下文到根IOC容器中。
2)加载spring配置文件:<context-param>标签中param-name为contextConfigLocation 的配置文件,到根IOC容器中。
3)加载环境配置。
4)调用refresh()完成各组件初始化。
3 将创建好的根IOC容器放入ServletContext:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
SpringMVC子IOC容器初始化
从web应用启动总流程可以看出,servlet 在最后一步被初始化。
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
servlet在被初始化的时候会调用其init()方法。
从DispatchServlet类继承关系图中可以知道最终会调用HttpServletBean中init()方法,该初始化方法的主要作用:
将Servlet初始化参数(init-param)设置到该组件上(如contextAttribute、contextClass、namespace、contextConfigLocation),通过BeanWrapper简化设值过程,方便后续使用,同时调用模板方法initServletBean()完成以下操作:
1 initWebApplicationContext:创建子IOC容器。将ServletContext中name为 org.springframework.web.context.WebApplicationContext.ROOT 的Attribute(根IOC容器)取出,作为父容器。
2 createWebApplicationContext:通过反射,创建子IOC容器XmlWebApplicationContext,并设置上一步中的根容器作为父容器。
3 加载环境配置信息。
4 加载SpringMVC配置文件:<servlet>标签中param-name为:contextConfigLocation。
5 调用configureAndRefreshWebApplicationContext,最终调用refresh()完成各组件初始化。
最后将创建好的子容器放入ServletContext中:String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac);
总结
SpringMVC(源码中大量使用了模板方法模式)的整体初始化过程大致为:
1 web容器启动。
2 Spring根IOC容器初始化:
2.1)contextLoaderListener监听到容器初始化事件,调用contextInitialized()
方法。
2.2)创建根IOC容器。
2.3)设置全局上下文到根容器中。
2.4)加载Spring各项配置。
2.5)调用refresh方法完成Spring各项配置初始化。
2.6)将根IOC容器设置到全局上下文环境ServletContext的Attribute中。
3 初始化web.xml中Filter。
4 SpringMVC子IOC容器初始化(web.xml中DispatcherServlet):
4.1)调用servlet.init方法。
4.2)初始化参数。
4.3)创建子IOC容器。
4.4)加载SpringMVC各项配置。
4.5)从全局上下文环境ServletContext的Attribute中,取得根IOC容器并设置为父容器,建立父子容器关系。
4.6)调用refresh方法完成SpringMVC各项组件初始化。
4.7)将子IOC容器设置到全局上下文环境ServletContext的Attribute中。
Filter和Interceptor的本质区别在于:
Filter是Servlet层面,无法直接注入由容器管理的Bean。
Interceptor属于SpringMVC层面,可以直接注入由容器管理的Bean。