web与spring初始化过程分析
一.总体初始化流程
web.xml加载顺序,context-param -> listener -> filter -> servlet;
启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取
和 两个结点; 紧接着,容器创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文;
容器将
转换为键值对,并交给servletContext; 容器创建
中的类实例,创建监听器。(如spring的ContextLoadListener、RequestContextListener、Log4jConfigListener等)(ContextLoadListener实例化Ioc容器,根据条件决定是否此时实例化bean,如lazy-init,scope等参数); 构建filter链(如CharacterEncodingFilter、OpenSessionInViewFilter、struts2的StrutsPrepareFilter、StrutsExecuteFilter等);
注意:servlet是单实例多线程;spring bean默认也是单实例的,不过可以通过scope参数来设置为别的;struts2中action是多例的。另外,本文基于spring3.2.2源码来分析的。
二.详细解析
servlet、listener、filter的关键方法
Servlet
-init(ServletConfig config)
-service(ServletRequest req, ServletResponse res)
//service再根据请求类型调用doGet、doPost等方法
-destroy()
Listener
ServletContextListener
-contextInitialized ( ServletContextEvent sce )
-contextDestroyed ( ServletContextEvent sce )
ServletRequestListener
-requestInitialized(ServletRequestEvent requestEvent)
-requestDestroyed(ServletRequestEvent requestEvent)
Filter
-init(FilterConfig filterConfig)
-destroy()
-doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
1.Spring-ContextLoaderListener
ContextLoaderListener
作用就是启动Web容器时,自动装配spring的配置文件的配置信息;ContextLoaderServlet
:与ContextLoaderListener功能一样;
ContextLoaderListener它继承了javax.servlet.ServletContextListener
接口。ServletContextListener是J2EE Servlet API中的一个标准接口,它能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。
Ioc容器实例化流程如图所示:
实例化Spring IoC容器的过程中,最主要的两个方法是createWebApplicationContext
和configureAndRefreshWebApplicationContext
方法。
createWebApplicationContext
方法用于返回XmlWebApplicationContext实例,即Web环境下的Spring IoC容器。
configureAndRefreshWebApplicationContext
用于配置XmlWebApplicationContext,读取web.xml中通过contextConfigLocation
标签指定的XML文件,实例化XML文件中配置的bean,并在上一步中实例化的容器中进行注册。
完成以上两步的操作后,Spring MVC会将XmlWebApplicationContext实例以属性的方式注册到ServletContext中。此Spring 容器是ROOT上下文,供所有的Spring MVC Servlcet使用。
核心的初始化逻辑都在AbstractApplicationContext的refresh()
方法里:
1 | public void refresh(){ |
obtainFreshBeanFactory()方法会刷新所有BeanFactory子容器,在此会加载、解析Bean的定义。
finishBeanFactoryInitialization()方法用来实例化bean。
bean实例化时机:
若是单例、lazy-init=false且不是抽象类,则实例化,否则等到执行时才实例化;实例化中若有依赖别的bean,需要先实例化依赖的bean。
具体代码逻辑在DefaultListableBeanFactory
的preInstantiateSingletons()
和AbstractBeanFactory
的doGetBean()
,可以参考Spring 框架的设计理念与设计模式分析。
ApplicationContext vs BeanFactory
ApplicationContext的类继承关系如图所示:ApplicationContext
由BeanFactory 派生而来,提供了更多面向实际应用的功能。几乎所有的应用场合我们都直接使用ApplicationContext
而非底层的BeanFactory。在BeanFactory 中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置的方式实现。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。
WebApplicationContext
是专门为Web 应用准备的,WebApplicationContext 扩展了ApplicationContext。
Spring 分别提供了用于启动WebApplicationContext 的Servlet 和Web 容器监听器:
org.springframework.web.context.ContextLoaderServlet;
org.springframework.web.context.ContextLoaderListener;
两者的内部都实现了启动WebApplicationContext 实例的逻辑,我们只要根据Web 容器的具体情况选择两者之一,并在web.xml 中完成配置就可以了。
2.SpringMVC-DispatcherServlet
DispatcherServlet配置在web.xml中,是springmvc的入口,它的继承关系图如下图所示:
- HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法,该初始化方法的主要作用:
a. 将Servlet初始化参数(init-param)设置到该组件上(如contextAttribute、contextClass、namespace、contextConfigLocation),通过BeanWrapper简化设值过程,方便后续使用;
b. 提供给子类初始化扩展点,initServletBean(),该方法由FrameworkServlet覆盖。 - FrameworkServlet继承HttpServletBean,通过initServletBean()进行Web上下文初始化,该方法主要覆盖一下两件事情:
a. 初始化web上下文;
b. 提供给子类初始化扩展点; - DispatcherServlet继承FrameworkServlet,并实现了onRefresh()方法提供一些前端控制器相关的配置:
1 | public class DispatcherServlet extends FrameworkServlet { |
//初始化默认的Spring Web MVC框架使用的策略(如HandlerMapping)1
2
3
4
5
6
7
8
9
10
11
12protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
}
从如上代码可以看出,DispatcherServlet启动时会进行我们需要的Web层Bean的配置,如HandlerMapping、HandlerAdapter等,而且如果我们没有配置,还会给我们提供默认的配置。
从如上代码我们可以看出,整个DispatcherServlet初始化的过程和做了些什么事情,具体主要做了如下两件事情:
- 初始化Spring Web MVC使用的Web上下文,并且可能指定父容器为(ContextLoaderListener加载了根上下文);
- 初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。
3.Spring-RequestContextListener
ContextLoaderListener实现ServletContextListener监听器接口,而ServletContextListener只负责监听Web容器的启动和关闭的事件。RequestContextListener实现ServletRequestListener监听器接口,该监听器监听HTTP请求事件,Web服务器接收的每次请求都会通知该监听器。
4.其它的一些类
1)CharacterEncodingFilter:字符编码转换filter,这个filter一定要注意顺序,要放在其它filter前面,要不然可能不起作用;
2) OpenSessionInViewFilter:主要功能是用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。跟延迟加载相关。
3) Struts2的入口:StrutsPrepareFilter、StrutsExecuteFilter、StrutsListener
三. 参考文献
- web.xml加载顺序
- Spring 框架的设计理念与设计模式分析
- Servlet 工作原理解析
- 第三章 DispatcherServlet详解 ——跟开涛学SpringMVC
- <深入分析JAVA web技术内幕-许令波>
- spring docs