Spring与Spring MVC父子容器
1、Spring与Spring MVC父子容器概念介绍
在Spring与Spring MVC进行整合的时候,一般情况下我们会使用不同的配置文件来配置Spring和Spring MVC,因此我们的应用中会存在至少2个ApplicationContext的实例,由于是在Web应用中,因此最终实例化的是ApplicationContext的子接口WebApplicationContext。如下图所示:
上图中显示了2个WebApplicationContext实例,为了进行区分,分别称之为:Servlet WebApplicationContext和Root WebApplicationContext。其中:
1)Servlet WebApplicationContext:这是对J2EE三层架构中的Web层进行配置,如控制器(Controller)、视图解析器(View Resolvers)等相关的Bean。通过Spring MVC中提供的DispatchServlet来加载配置,通常情况下,配置文件的名称为spring-servlet.xml。
2)Root WebApplicationContext:这是对J2EE层架构中的Service层、Dao层进行配置,如业务Bean,数据源(DataSource)等。通常请客下,配置文件的名称为applicationContext.xml。在Web应用中,其一般通过ContextLoaderListener来加载。
以下是一个Web.xml配置案例,代码如下图所示:
在上面的配置中:
1)ContextLoaderListener会被优先初始化,其会根据<context-param>元素中contextConfigLocation参数指定的配置文件路径,在这里就是"/WEB-INF/spring/applicationContext.xml”,来创建WebApplicationContext实例。并调用ServletContext的setAttribute方法,将其设置到ServletContext中,属性的key为”org.springframework.web.context.WebApplicationContext.ROOT”,最后的”ROOT"字样表明这是一个 Root WebApplicationContext。
2)DispatcherServlet在初始化时,会根据<init-param>元素中contextConfigLocation参数指定的配置文件路径,即"/WEB-INF/spring/spring-servlet.xml”,来创建Servlet WebApplicationContext。同时,其会调用ServletContext的getAttribute方法来判断是否存在Root WebApplicationContext。如果存在,则将其设置为自己的parent。这就是父子上下文(父子容器)的概念。
父子容器的作用在于,当我们尝试从子容器(即Servlet WebApplicationContext)中获取一个Bean时,如果找不到,则会委派给父容器(即Root WebApplicationContext)来查找。
如果我们没有通过ContextLoaderListener来创建Root WebApplicationContext,那么Servlet WebApplicationContext的parent就是null,也就是没有父容器。
2、为什么要有父子容器
(1)划分框架边界
在J2EE三层架构中,在Service层我们一般使用Spring框架, 而在Web层则有多种选择,如Spring MVC、Struts等。因此,通常对于Web层我们会使用单独的配置文件。例如在上面的案例中,一开始我们使用spring-servlet.xml来配置Web层,使用applicationContext.xml来配置Service层、DAO层。如果现在我们想把Web层从Spring MVC替换成Struts,那么只需要将spring-servlet.xml替换成Struts的配置文件struts.xml即可,而applicationContext.xml不需要改变。
事实上,如果你的项目确定了只使用Spring和Spring MVC的话,你甚至可以将Service层、DAO层、Web层的Bean都放到spring-servlet.xml中进行配置,并不是一定要将Service层、DAO层的配置单独放到applicationContext.xml中,然后使用ContextLoaderListener来加载。在这种情况下,就没有了Root WebApplicationContext,只有Servlet WebApplicationContext。
(2)公共资源统一管理
由配置文件可以看出Web应用中可以配置多个Servlet WebApplicationContext,对于那些共享的DAO和Service,如果每个Servlet WebApplicationContext都去配一遍就显得多余,我们可以把这些配置在Root WebApplicationContext里做统一管理。
3、Root WebApplicationContext创建过程源码分析
ContextLoaderListener用于创建Root WebApplicationContext,其实现了ServletContextListener接口的contextInitialized和contextDestroyed方法,在Web应用启动和停止时,Web容器(如Tomcat)会负责回调这两个方法。而创建Root WebApplicationContext就是在contextInitialized中完成的,代码如下图所示:
可以看到ContextLoaderListener继承了ContextLoader类,真正的创建是在ContextLoader的initWebApplicationContext方法中完成的,代码如下图所示:
4、Servlet WebApplicationContext创建过程源码分析
DispatcherServlet负责创建Servlet WebApplicationContext,并尝试讲ContextLoaderListener创建的Root WebApplicationContext设置为自己的parent。其类图继承关系如下所示:
其中HttpServletBean继承HttpServlet,因此在应用初始化时,其init方法会被调用,代码如下图所示:
HttpServletBean的init方法中,调用了initServletBean()方法,在HttpServletBean中,这个方法是空实现。FrameworkServlet覆盖了HttpServletBean中的initServletBean方法,代码如下图所示:
上述代码片段中,我们可以看到通过调用FrameworkServlet的另一个方法initWebApplicationContext来真正创建WebApplicationContext实例,代码如下图所示:
转自:http://www.tianshouzhi.com/api/tutorials/spring