Spring源码之父子容器

1. 介绍

Spring父子容器是指Spring IOC容器中存在另一个IOC容器,其两者并不是继承关系,而是在AbstractApplicationContext类中存在一个ApplicationContext类型的成员变量,变量名为parent,所以称之为父子容器;其特点是子容器可以访问父容器中的BeanDefinition实例Bean实例,反过来父容器访问子容器则不行,如Spring MVCSpring就是一对父子容器;在Spring官方中称父子容器为Servlet WebApplicationContextRoot WebApplicationContext

1.1 为什么需要父子容器?

父子容器的主要作用是划分框架边界。因为在日常J2EE三层架构中,servicedao层的实例一般是交由Spring来管理的,而web层则有多种选择,如Spring MVCStrutsStruts2等等,因此对于web层我们一般使用单独的配置文件来管理其实例,在对web层的框架进行更换时也不用去修改servicedao层的代码和配置;而且在J2EE架构中,web层通常需要注入service层的实例,所以web层作为子容器,service层作为父容器,也符合子容器可以访问父容器的实例的规则;

1.2 是否可以把所有的类通过父容器来管理?

不行,Spring MVC在解析@RequestMapping注解时只会对子容器中的Bean实例进行处理,并没有去查找父容器的Bean实例,所以在请求接口时会产生404

1.3 是否可以把所有的类通过子容器来管理?

可以,但不推荐;而且如果在父容器中有AOP或者事务配置,也得一起迁移到子容器的配置文件中来,否则将产生AOP或者事务不生效的情况;

1.4 是否可以把所有的类在父子容器中同时管理?

一般不采用这种方式配置,原因一是同样的实例在父子容器中都存在一份造成了资源浪费,原因二是子容器在注入service层实例时不会采用父容器中的实例,这样如果在父容器中配置有AOP或者事务也会导致其失效;

1.5 Spring Boot中是否存在父子容器?

不存在,Spring Boot中所有的Bean是由其源码中的AnnotationConfigEmbeddedWebApplicationContext容器管理的,这个类是WebApplicationContext接口的子类;


2. 源码

2.1 父容器的创建过程

父容器的配置

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

入口

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
	
	public ContextLoaderListener() {
	
	}

	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}

    // 这个方法会被Servlet调用
	@Override
	public void contextInitialized(ServletContextEvent event) {
        // 初始化RootWebApplicationContext
		initWebApplicationContext(event.getServletContext());
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

初始化RootWebApplicationContext

public class ContextLoader {
    
    // ......
    
    @Nullable
	private WebApplicationContext context;
    
    // ......
	
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			if (this.context == null) {
                /*
                 * 创建RootWebApplicationContext实例
                 * 实例Class类型默认从Servlet上下文的初始化参数contextClass中获取
                 * 若未获取到则从默认策略(ContextLoader.properties配置文件)中获取
                 */ 
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
                    // 配置并刷新RootWebApplicationContext, Bean实例创建就是在这里
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
            // 把RootWebApplicationContext以org.springframework.web.context.WebApplicationContext.ROOT为名称存入Servlet上下文中(重要)
			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);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}
	
}

2.2 子容器的创建过程

DispatcherServlet继承体系

在这里插入图片描述

初始化方法init()HttpServletBean类中

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

    // ......

	@Override
	public final void init() throws ServletException {
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// 初始化ServletBean
		initServletBean();
	}

	protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
	}

    // 由子类实现
	protected void initServletBean() throws ServletException {
	}

	// ......

}

初始化ServletBean

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    // ......

	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		long startTime = System.currentTimeMillis();

		try {
            // 初始化ServletWebApplicationContext
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}

    // ......

    protected WebApplicationContext initWebApplicationContext() {
        // 从Servlet上下文中获取RootWebApplicationContext, 与RootWebApplicationContext创建后存入Servlet上下文相互呼应
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		if (wac == null) {
            // 创建ServletWebApplicationContext, 并将RootWebApplicationContext注入作为父容器
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			synchronized (this.onRefreshMonitor) {
                // 刷新ServletWebApplicationContext
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}
	
	// ......
	
}

2.3 子容器获取父容器Bean实例过程

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    
    // ......
    
    protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
        
		String beanName = transformedBeanName(name);
		Object beanInstance;

		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
            
			// ......

		else {
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// 获取父容器
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// 如果在当前容器中未找到BeanDefinition且父容器不为空, 则去父容器中获取实例
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}
            
            // ......

		}

		return adaptBeanInstance(name, beanInstance, requiredType);
	}
    
    // ......

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JackieGGu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值