我们在基于spring + spring mvc架构的项目的web.xml中看到如下的经典配置:(定义了两个配置xml文件,将servlet相关的对象定义在一个xml中;其他的定义在另一xml中,比如dao层对象,service对象等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/classes/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/mvc-applicationContext.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
在这段代码中,有pplicationContext.xml 和mvc-applicationContext.xml两个配置文件, 前者通过<context-param>配置,后者作为DispatcherServlet的参数,那么这两个配置文件是加载的,它们是构成一个ApplicationContext,还是构成多个呢,如果是构成两个ApplicationContext,那么这两个ApplicationContext是如何协调工作的?这就是本博文将要回答的问题。
其实所有的问题就藏在ContextLoaderListener 和 DispatcherServlet的源码当中!!!
ContextLoaderListener位于spring-web-3.1.1.RELEASE.jar中,
ServletContextListener实现了ServletContextListener接口,系统启动时会回调contextInitialized()方法。
1 2 3 4 5 6 7 |
public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); } |
将加载的任务委托给了ContextLoader.initWebApplicationContext()方法,该方法的主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext); } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } return this.context; } |
在这个方法中,利用context-param中指定的配置文件初始化ApplicationContext,然后以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key,注册为servletContext的一个属性。
DispatcherServlet位于spring-webmvc-3.1.1.RELEASE.jar中
DispatcherServlet的继承关系为:
DispatcherServlet
|--FrameworkServlet
|--HttpServletBean
initServletBean将初始化的任务委托给initWebApplicationContext()
1 2 3 4 |
protected final void FrameworkServlet::initServletBean() throws ServletException { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } |
在initFrameworkServlet()中包含如下代码
1 2 |
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); |
这个函数的作用就是从ServletContext中获取以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key的ApplicationContext对象,如果返回的值不为空,将其作为新创建的ApplicationContext的parent。具体的逻辑在createWebApplicationContext(ApplicationContext parent)中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); configureAndRefreshWebApplicationContext(wac); return wac; } |
在这个方法中,创建了一个ApplicationContext对象wac,将其parent设置为传入的ApplicationContext,也就是使用<context-param>中指定的配置文件初始化的ApplicationContext。然后使用DispatcherServlet自身的ApplicationContext初始化wac。
我们的示例中,spring初始化了两个ApplicationContext,<context-param>中指定的配置文件(applicationContext.xml)初始化的ApplicationContext是DispatcherServlet中指定的配置文件(mvc-applicationContext.xml)初始化的ApplicationContext的父Context。
Posted in: spring practise
Comments are closed.