SpringMVC中的RootWebApplicationContext与ServletWebApplicationContext区别

22 篇文章 1 订阅
6 篇文章 0 订阅


RootWebApplicationContext 以下简称RootContext
ServletWebApplicationContext 以下简称ServletContext

官网解释

我们在Spring MVC官方文档上可以看到两个容器有父子的关系,SpringMVC中两个WebApplicationContext的继承关系。RootContext会被注入到ServletContext的parentBeanFactory中。SpringMVC的容器ServletContext中如果没有bean,则委派RootContext中去查找,也就是SevletContext中可以访问RootContext中的bean,反过来则不行

在这里插入图片描述
通常情况下我们会在web.xml中进行这样配置:
使用org.springframework.web.context.ContextLoaderListenerorg.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)
	...
	...

如有不对的地方,欢迎指出纠正,每天进步一点点!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

huihttp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值