SpringMVC中的RootWebApplicationContext与ServletWebApplicationContext区别

这里写目录标题

    • 官网解释
    • 代码中获取两个容器

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

官网解释

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

SpringMVC中的RootWebApplicationContext与ServletWebApplicationContext区别_第1张图片
通常情况下我们会在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.ContextLoaderListenerlistener-class>
    listener>

    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>/WEB-INF/root-context.xmlparam-value>
    context-param>

    <servlet>
        <servlet-name>app1servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>/WEB-INF/mvc-context.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>

    <servlet-mapping>
        <servlet-name>app1servlet-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.
	 * 

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)
	...
	...

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

你可能感兴趣的:(java基础,Spring框架,笔记)