Spring ApplicationContext上下文核心容器深入探究

 更新时间:2023年01月10日 11:26:23   作者:赵韩兵先生  
ApplicationContext是Spring应用程序中的中央接口,由于继承了多个组件,使得ApplicationContext拥有了许多Spring的核心功能,如获取bean组件,注册监听事件,加载资源文件等

Spring 容器核心可归纳为两个类: BeanFactory 和 ApplicationContext,ApplicationContext 继承自 BeanFactory ,其不仅包含 BeanFactory 所有功能,还扩展了容器功能。ApplicationContext 核心其实是 refresh 方法,容器一系列功能都在该方法中实现,如:注册 Bean、注入 Bean 等。

整体流程

refresh 方法定义在 ConfigurableApplicationContext 接口中,被 AbstractApplicationContext 抽象类实现,该方法由十几个子方法组成,这些子方法各司其职完成容器的初始化。

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
	......
	//加载或刷新配置的持久表示形式,
    //可能是XML文件、属性文件或关系数据库模式。
    //由于这是一个启动方法,它应该销毁已经创建的单例
    //如果失败,则避免悬而未决的资源。换句话说,在调用该方法之后,要么全部实例化,要么根本不实例化。
    //如果无法初始化bean工厂,则抛出BeanException异常,抛出IllegalStateException异常
    //如果已初始化且不支持多次刷新尝试
	void refresh() throws BeansException, IllegalStateException;
    ......
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    ......
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备此上下文以进行刷新
			prepareRefresh();
			// 告诉子类刷新内部bean工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 准备bean工厂,以便在此上下文中使用
			prepareBeanFactory(beanFactory);
			try {
				// 允许在上下文子类中对bean工厂进行后处理
				postProcessBeanFactory(beanFactory);
				// 调用在上下文中注册为bean的工厂处理器
				invokeBeanFactoryPostProcessors(beanFactory);
				// 注册拦截bean创建的bean处理器
				registerBeanPostProcessors(beanFactory);
				// 为此上下文初始化消息源。
				initMessageSource();
				// 为此上下文初始化事件多播
				initApplicationEventMulticaster();
				// 初始化特定上下文子类中的其他特殊bean
				onRefresh();
				// 检查侦听器bean并注册它们
				registerListeners();
				// 实例化所有剩余的(非懒加载)单例
				finishBeanFactoryInitialization(beanFactory);
				// 最后一步:发布相应的事件
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// 销毁已创建的单例以避免悬空资源
				destroyBeans();
				// 重置“活动”标志
				cancelRefresh(ex);
				// 将异常传播到调用方
				throw ex;
			}
			finally {
				// 重置Spring核心中的常见内省缓存,因为我们可能不再需要单例bean的元数据。。。
				resetCommonCaches();
			}
		}
	}
 ......
}

在spring中AbstractApplicationContext的实现类通过调用父类的方法完成容器的初始化,下文以ClassPathXmlApplicationContext为例来追踪容器的创建流程。

public class contextText {
	ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
}
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
......
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
......
}

refresh 方法中,前四个子方法主要进行上下文准备工作。

prepareRefresh

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 初始化上下文环境,就是记录下容器的启动时间、活动状态等
		prepareRefresh();
		...
    }
}
public abstract class AbstractApplicationContext {
    ...
    private long startupDate;
    private final AtomicBoolean active = new AtomicBoolean();
	private final AtomicBoolean closed = new AtomicBoolean();
    private Set<ApplicationEvent> earlyApplicationEvents;
    ...
    protected void prepareRefresh() {
        // 记录此上下文开始时的系统时间(以毫秒为单位)
    	this.startupDate = System.currentTimeMillis();
    	// 记录此上下文是否已关闭,这里设置为未关闭
    	this.closed.set(false);
    	// 记录此上下文是否处于活动状态,这里设置为活动状态
    	this.active.set(true);
    	if (logger.isInfoEnabled()) {
    		logger.info("Refreshing " + this);
    	}
    	// 这也是交由子类扩展的方法。具体子类为 GenericWebApplicationContext,主要是初始化属性源,
    	// 将 ServletContext 和 ServletConfig 属性配置添加到 Environment 环境上下文中
    	initPropertySources();
    	// 校验 Environment 中那些必备的属性配置是否存在,不存在则抛异常。
    	getEnvironment().validateRequiredProperties();
    	// 创建 ApplicationEvent 事件集合
    	this.earlyApplicationEvents = new LinkedHashSet<>();
    }
}

refresh 中的 prepareRefresh 方法执行结束,主要是记录容器的启动时间、活动状态、检查必备属性是否存在。

obtainFreshBeanFactory

public abstract class AbstractApplicationContext {
    ...
    public void refresh() throws BeansException, IllegalStateException {
    	synchronized (this.startupShutdownMonitor) {
    		...
    		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    		...
    	}
    }
    ...
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 该方法也是由子类扩展,其子类有 AbstractRefreshableApplicationContext 和 GenericApplicationContext,
        // 该方法主要设置 BeanFactory 的 serializationId 属性值,也就是序列化id
    	refreshBeanFactory();
    	// 通过 getBeanFactory 返回 BeanFactory 对象。同样也是由子类扩展,调用的是 GenericApplicationContext 类中的 getBeanFactory 方法。
    	// 返回的是 DefaultListableBeanFactory 。
    	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    	if (logger.isDebugEnabled()) {
    		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    	}
    	return beanFactory;
    }
    ...
}

obtainFreshBeanFactory 方法很简单,因为当前使用的是ClasspathXmlApplicationContext容器类,从类的继承关系可以看出执行的就是 AbstractRefreshableApplicationContext 中的 refreshBeanFactory 方法,返回的是DefaultListableBeanFactory,之后该方法还返回了 BeanFactory 对象,从这也可以看出 ApplicationContext 底层是以 BeanFactory 为基础,逐步扩展 Spring 容器功能。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
   ......
	/** Bean factory for this context */
	@Nullable
	private DefaultListableBeanFactory beanFactory;
    ......
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
			return this.beanFactory;
		}
	}
    ......
}

prepareBeanFactory

prepareBeanFactory 方法主要是对 BeanFactory 做一些配置,包含各种类加载器、需要忽略的依赖以及后置处理器、解析器等

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        ...
		prepareBeanFactory(beanFactory);
		...	
    }
    ...
}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 设置类加载器
	beanFactory.setBeanClassLoader(getClassLoader());
	// 设置表达式解析器,主要用来解析 EL 表达式; Bean 初始化完成后填充属性时会用到
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	// 设置属性注册解析器,主要用来解析 Bean 中的各种属性类型,如 String、int 等
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
	// 添加一个后置处理器:ApplicationContextAwareProcessor。
	// 该后置处理器用于向实现了 Aware 系列接口的 bean 设置相应属性。
	// (后置处理器和 Aware 接口也是比较核心的概念,后面会有文章详细讨论)
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	// 以下接口,在自动注入时会被忽略,其都是 Aware 系列接口
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
	// 当以下特殊的 Bean 需自动注入时,指定其注入的类型 。
	// 如:注入 BeanFactory 时,注入的类型对象为 ConfigurableListableBeanFactory 。
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);
	// 添加 ApplicationListenerDetector 后置处理器。
	// 该后置处理器用来检测那些实现了 ApplicationListener 接口的 bean,并将其添加到应用上下文的事件广播器上。
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
	// 判断容器中是否存在 loadTimeWeaver Bean,如果存在则上下文使用临时的 ClassLoader 进行类型匹配。
	// 集成 AspectJ 时会用到 loadTimeWeaver 对象。
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}
	// 注册和环境相关的 Bean,如 environment、systemProperties、systemEnvironment
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}

在 prepareBeanFactory 方法中,主要对 BeanFactory 添加了一系列属性项,如添加忽略自动注入的接口、添加 BeanPostProcessor 后置处理器、手动注册部分特殊的 Bean及环境相关的 Bean 。

postProcessBeanFactory

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        ...
        postProcessBeanFactory(beanFactory);
        ...   
    }
}

postProcessBeanFactory 方法是上下文准备的最后一步,spring中并没有具体去实现postProcessBeanFactory方法,是提供给想要实现BeanPostProcessor的三方框架使用的。谁要使用谁就去实现。作用是在BeanFactory准备工作完成后做一些定制化的处理,一般结合BeanPostProcessor接口的实现类一起使用,注入一些重要资源(类似Application的属性和ServletContext的属性)。最后需要设置忽略这类BeanPostProcessor子接口的自动装配。

总结

ApplicationContext 上下文准备工作基本结束,主要还是在 BeanFactory 中添加一系列后置处理器、注册特殊的 Bean 及设置忽略自动注入的接口。其中还提到了 Spring 容器的三个核心部分:Aware 系列接口、BeanPostProcessor 后置处理器、BeanDefinition ,这部分在后面的文章会逐步跟踪。

到此这篇关于Spring ApplicationContext上下文核心容器深入探究的文章就介绍到这了,更多相关Spring ApplicationContext内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Aop之AspectJ注解配置实现日志管理的方法

    Spring Aop之AspectJ注解配置实现日志管理的方法

    下面小编就为大家分享一篇Spring Aop之AspectJ注解配置实现日志管理的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • 使用DevOps开源利器开发部署Hygieia平台

    使用DevOps开源利器开发部署Hygieia平台

    这篇文章主要为大家介绍了使用DevOps开源利器开发部署Hygieia平台的实现,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职 加薪
    2022-03-03
  • SpringBoot+Vue前后端分离实现审核功能的示例

    SpringBoot+Vue前后端分离实现审核功能的示例

    在实际开发中,审核功能是一个非常常用的功能,本文就来介绍一下使用SpringBoot+Vue前后端分离实现审核功能的示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • java 求解二维数组列最小值

    java 求解二维数组列最小值

    这篇文章主要介绍了java 求解二维数组列最小值的相关资料,需要的朋友可以参考下
    2017-05-05
  • idea创建spring boot工程及配置文件(最新推荐)

    idea创建spring boot工程及配置文件(最新推荐)

    本文给大家介绍idea创建spring boot工程及配置文件,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • 解决Java中的java.io.IOException: Broken pipe问题

    解决Java中的java.io.IOException: Broken pipe问题

    这篇文章主要介绍了解决Java中 java.io.IOException: Broken pipe的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • 浅聊一下Spring中Bean的配置细节

    浅聊一下Spring中Bean的配置细节

    我们知道,当写完一个普通的 Java 类后,想让 Spring IoC 容器在创建类的实例对象时使用构造方法完成实例对象的依赖注入,那么就需要在配置元数据中写好类的 Bean 定义,包括各种标签的属性。所以本文我们来说说这其中的配置细节,需要的朋友可以参考下
    2023-07-07
  • spring cloud实现前端跨域问题的解决方案

    spring cloud实现前端跨域问题的解决方案

    这篇文章主要介绍了 spring cloud实现前端跨域问题的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • java中每月等额与先息后本计算

    java中每月等额与先息后本计算

    一般信用贷款会提供两种还款方式:每月等额或者先息后本。每月等额,就是每月归还等同的部分本金和利息,你手里在使用的本金其实是逐月减少的。先息后本就是先还利息,到期归还本金。本文将介绍他们的实现方法。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-03-03
  • java双色球机选法程序解析

    java双色球机选法程序解析

    这篇文章主要为大家详细解析了java双色球机选法程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01

最新评论