Spring 源码阅读 02:ApplicationContext 初始化 Spring 容器

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文主要分析了通过 ClassPathXmlApplicationContext 初始化 Spring 容器的过程。

基于 Spring Framework v5.2.6.RELEASE

在使用 XML 文件初始化 Spring 容器时,通常会用到以下的方式:

ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");
Useruser=context.getBean("user", User.class);
user.sayHello();

其中,beans.xml 文件中定义了一些 Bean,在 ClassPathXmlApplicationContext 的构造方法中,通过读取 beans.xml 中的配置,创建了 Spring 容器。ClassPathXmlApplicationContextApplicationContext 的实现类,ApplicationContext 继承自 BeanFactory。我们可以通过它的 getBean 方法,从 Spring 容器中获取 Bean 对象。

ClassPathXmlApplicationContext 的构造方法

我们从 ClassPathXmlApplicationContext 的构造方法开始,在源码中探索 XML 配置文件是如何加载进来的。

上面的代码中,我们使用了以下的构造方法:

publicClassPathXmlApplicationContext(StringconfigLocation) throwsBeansException {
this(newString[] {configLocation}, true, null);
}

在这里,调用了另外一个构造方法,定义如下:

publicClassPathXmlApplicationContext(
String[] configLocations, booleanrefresh, @NullableApplicationContextparent)
throwsBeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
   }
}

综合看以上的两个构造方法,这里使用了三个参数:

  • configLocations 是我们创建 ClassPathXmlApplicationContext 时提供的配置文件路径,从方法签名中看出,这里可以提供多个配置文件。
  • refresh 表示是否自动刷新上下文。这里刷新上下文还包括重新加载所有的 BeanDefinition、创建单例 Bean 等。这里的代码中调用方法时的值时 true
  • parent 是当前容器的父容器,这里代码中直接提供了 null

接下来我们看方法体中的内容,主要有三句代码,我们一一来探索。

ClassPathXmlApplicationContext 构造方法中的内容

调用父类构造方法

第一步,以 parent 为参数调用了父类的构造方法。下图是 ClassPathXmlApplicationContext 类的继承关系图:

image.png

顺着父类构造方法的调用一路跟踪,我们可以找到以下代码:

publicAbstractApplicationContext(@NullableApplicationContextparent) {
this();
setParent(parent);
}

这里最终调用到了 AbstractApplicationContext 的构造方法,其中有两行逻辑。

  1. 调用了无参构造方法
  2. 调用了 setParent(parent)

我们分别来看。

无参构造方法的代码如下:

publicAbstractApplicationContext() {
this.resourcePatternResolver=getResourcePatternResolver();
}
protectedResourcePatternResolvergetResourcePatternResolver() {
returnnewPathMatchingResourcePatternResolver(this);
}

这里初始化了 resourcePatternResolver 成员变量,PathMatchingResourcePatternResolver 是一个基于路径匹配的资源解析器。

之后的 setParent 方法源码如下:

publicvoidsetParent(@NullableApplicationContextparent) {
this.parent=parent;
if (parent!=null) {
EnvironmentparentEnvironment=parent.getEnvironment();
if (parentEnvironmentinstanceofConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
      }
   }
}

这里将传进来的 parent 参数复制给了对应的成员变量。因为前面的代码中,实际传入的值为 null,因此之后的 if 语句块中的内容我们先不看。

setConfigLocations 处理配置文件路径中的占位符

ClassPathXmlApplicationContext 构造方法的第二步,调用了 setConfigLocations 方法:

publicvoidsetConfigLocations(@NullableString... locations) {
if (locations!=null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations=newString[locations.length];
for (inti=0; i<locations.length; i++) {
this.configLocations[i] =resolvePath(locations[i]).trim();
      }
   }
else {
this.configLocations=null;
   }
}

这里的主要步骤,是把我们实例化 ClassPathXmlApplicationContext 时传递的配置文件路径,放到了一个新创建的字符串数组中,并将其赋值给 configLocations 成员变量。

在放入新输入之前,还调用了 resolvePath 方法对文件路径字符串进行了处理,我们看一下这个方法的源码:

protectedStringresolvePath(Stringpath) {
returngetEnvironment().resolveRequiredPlaceholders(path);
}

再通过 resolveRequiredPlaceholders 方法的接口定义,我们可以了解到,这里主要是为了处理文本中的 ${} 占位符。也就是说,这里给定的路径中可以包含 ${} 占位符,并且在创建 Spring 容器时,Spring 可以处理这些占位符,前提是,在当前的 Spring 环境中,能通过这些占位符找到匹配的值。

refresh 核心方法

在创建容器的构造方法中,refresh 是最核心的方法,这个方法在 AbstractApplicationContext 中定义。我们先看看 refresh 方法的源码:

@Overridepublicvoidrefresh() throwsBeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.// 初始化上下文的信息prepareRefresh();
// Tell the subclass to refresh the internal bean factory.// 这里会调用模版方法,通过子类的实现,初始化 BeanFactory 并解析 XML 配置ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.// 对 BeanFactory 进行初始配置prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.// 这里默认是一个空的模版方法,子类可以在此处实现逻辑postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.// 执行 BeanFactory 的后处理器invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.// 注册 Bean 的后处理器registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.// 初始化消息源initMessageSource();
// Initialize event multicaster for this context.// 初始化时间广播器initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.// 这里也是一个空的模版方法,子类可以在此处实现逻辑,在容器中的单例 Bean 初始化之前,来初始化一些特殊的 BeanonRefresh();
// Check for listener beans and register them.// 注册化监听器registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.// 完成 BeanFactory 初始化,实例化所有非延迟加载的单例 BeanfinishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.// 初始化结束,广播相应的事件finishRefresh();
      }
catch (BeansExceptionex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - "+"cancelling refresh attempt: "+ex);
         }
// Destroy already created singletons to avoid dangling resources.destroyBeans();
// Reset 'active' flag.cancelRefresh(ex);
// Propagate exception to caller.throwex;
      }
finally {
// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();
      }
   }
}

为了方便理解,我在代码里添加了中文注释,如果你对 Spring 容器初始化的流程有大概的了解,就可以发现,在这个方法中,包含了 Spring 容器初始化的所有核心步骤。

本文先介绍这么多,后面会对 refresh() 方法的详细流程进行探索。

目录
相关文章
|
5天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
5天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
20 0
|
5天前
|
XML Java 数据格式
【spring】01 Spring容器研究
【spring】01 Spring容器研究
10 0
|
5天前
|
前端开发 Java 容器
家族传承:Spring MVC中父子容器的搭建与管理指南
家族传承:Spring MVC中父子容器的搭建与管理指南
26 3
|
5天前
|
存储 程序员 C++
C++容器初始化方式详解:优缺点、性能与应用场景
C++容器初始化方式详解:优缺点、性能与应用场景
21 0
|
5天前
|
Java 容器 Spring
【spring(一)】核心容器总结
【spring(一)】核心容器总结
|
5天前
|
Java 开发者 容器
【Java】深入了解Spring容器的两个关键组件
【Java】深入了解Spring容器的两个关键组件
12 0
|
5天前
|
监控 Kubernetes Docker
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
【5月更文挑战第9天】本文探讨了Docker容器中应用的健康检查与自动恢复,强调其对应用稳定性和系统性能的重要性。健康检查包括进程、端口和应用特定检查,而自动恢复则涉及重启容器和重新部署。Docker原生及第三方工具(如Kubernetes)提供了相关功能。配置检查需考虑检查频率、应用特性和监控告警。案例分析展示了实际操作,未来发展趋势将趋向更智能和高效的检查恢复机制。
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
|
3天前
|
Java 虚拟化 Docker
Docker简介及用途,为什么要使用Docker?Docker容器和虚拟机的区别
Docker简介及用途,为什么要使用Docker?Docker容器和虚拟机的区别
|
3天前
|
存储 Linux Docker
CentOS7修改Docker容器和镜像默认存储位置
CentOS7修改Docker容器和镜像默认存储位置