RootWebApplicationContext 以下简称RootContext
ServletWebApplicationContext 以下简称ServletContext
官网解释
我们在Spring MVC官方文档上可以看到两个容器有父子的关系,SpringMVC中两个WebApplicationContext的继承关系。RootContext会被注入到ServletContext的parentBeanFactory中。SpringMVC的容器ServletContext中如果没有bean,则委派RootContext中去查找,也就是SevletContext中可以访问RootContext中的bean,反过来则不行
通常情况下我们会在web.xml中进行这样配置:
使用org.springframework.web.context.ContextLoaderListener和org.springframework.web.servlet.DispatcherServlet分别使用不同的XML配置文件初始化Spring和Spring MVC容器,这时候就会生成RootContext和ServletContext两个容器,对应关系为:
RootContext 是Spring的容器
SevletContext是Spring MVC的容器
以下web.xml配置示例注册并初始化DispatcherServlet:
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
注意:这里需要注意的一点,如果使用xml的模糊匹配,两个容扫描了同一个xml文件,
这个被重复扫描的xml文件中声明的bean可能会被加载两次,
例如:都写成了*-context.xml扫描到了定时器配置文件task-context.xml,
这时候就会启动双份的定时器,所有定时器会被执行两遍
**Servlet WebApplicationContext**:这是对J2EE三层架构中的web层进行配置,如控制器(controller)、视图解析器(view resolvers)等相关的bean。通过spring MVC中提供的DispatchServlet来加载配置,通常情况下,配置文件的名称为spring-servlet.xml。 **Root WebApplicationContext**:这是对J2EE三层架构中的service层、dao层进行配置,如业务bean,数据源(DataSource)等。通常情况下,配置文件的名称为applicationContext.xml。在web应用中,其一般通过ContextLoaderListener来加载。 ## 源码分析
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
。。。
try {
**1.这里进行初始化**
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
。。。
}
/**
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
**2.获取RootContext容器**
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
**3.设置到SevletContext容器中();**
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
。。。
}
代码中获取两个容器
下面是一些常用的获取方式
RootContext的获取方式
//方法一:(获取当前的spring容器,任何java类中适用)
ServletContext application = ServletActionContext.getServletContext();
ApplicationContext act = ContextLoader.getCurrentWebApplicationContext();
UserService userService = (UserService) act.getBean("userService");
//方法二:(重新加载spring容器) 不推荐
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
//方法三:(通过request或session加载spring容器)
ApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
SevletContext怎么获取呢?从下面代码我们就能看出来
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// Make framework objects available to handlers and view objects.
0000.从这里我们能看出来,从request中就可以取到SevletContext
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
}
所以我们这样取
//获取子容器sevlet, 不能获取父容器root,父容器中不存在service和dao实体bean
ServletRequestAttributes sevletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sevletRequestAttributes.getRequest();
WebApplicationContext servletContext = (WebApplicationContext)
request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
如果获取不到正确的容器就会报错,提示beanFactory找不到对应的bean对象,没有匹配到的bean
下面是错误展示,推荐获取SevletContext,因为SevletContext可以访问两个容器的所有bean对象
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [cn.wlyy.att.dao.PersonDao] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:371)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:975)
...
...
如有不对的地方,欢迎指出纠正,每天进步一点点!