Spring 源码阅读 17:初始化非懒加载的单例 Bean
基于 Spring Framework v5.2.6.RELEASE
前情提要
在之前的 ApplicationContext 初始化 Spring 容器 一文中,提到 AbstractApplicationContext#refresh
方法是一个非常重要的方法,它包含了 Spring 容器初始化的整个流程。最近的一系列文章都在深入分析这个方法中的每一个步骤的具体原理,本文接着分析 初始化非懒加载的单例 Bean 的流程,也就是refresh
方法中的这行代码:
finishBeanFactoryInitialization(beanFactory);
非懒加载 Bean 的初始化
这里有一个知识点需要先了解一下。
在 Spring 中,所有的普通单例 Bean 默认情况下都是不支持懒加载的,就是说,它会在容器初始化时被创建,这样还有一个好处就是,可以尽早发现配置中的错误。
在 XML 配置的bean
标签中,有一个lazy-init
属性,相应的,在 BeanDefinition 中也有一个lazyInit
成员变量,二者是对应的。它的默认值是false
。如果需要,我们也可以通过将这个值设置为true
,这样的话,这个单例 Bean 就只有在第一次从容器中获取它的时候,才会被创建并初始化,在此之前,容器中其实并不存在这个 Bean 的真正实例。
了解了这些之后,进入finishBeanFactoryInitialization
方法看详细代码:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
一些前置的工作
代码中的第一个if
语句块,是为了在beanFactory
中查找一个特定的 ConversionService 类型的 Bean,如果没有的话,就创建一个并注册到容器中。这是一个 Spring 用来处理类型转换的 Bean。
第二个if
语句块,用同样的方法注册了一个 StringValueResolver 类型的 Bean,它是一个用来处理注解属性的字符串内容解析器。我们还没有涉及到注解相关的部分,这里就先略过。
接下来是这部分代码:
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
这里和之前在 BeanFactory 预处理阶段的prepareBeanFactory方法中的以下代码片段对应:
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
主要是一些与 AspectJ 相关的流程,提前初始化了 LoadTimeWeaverAware 类型的 Bean,并把之前设置的 TempClassLoader 移除了。更多细节,我们这里也不做介绍。
方法的倒数第二行代码,调用了容器的freezeConfiguration
方法,也就是冻结配置。这个方法的作用就是将已经注册的 BeanDefinition 配置进行冻结,也就是,执行这个方法之后,这些 BeanDefinition 就不能再被做任何修改了。
初始化非懒加载的 Bean
方法的最后一行代码,才是关键的步骤,就是调用容器的preInstantiateSingletons
方法来初始化非懒加载的单例 Bean。方法的源码如下:
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
这个方法的代码量也不少,但是主要逻辑很简单。
从容器中拿到所有的beanName
,如果它不是抽象Bean、且是单例的、且被配置为不允许懒加载,那么就会通过getBean
方法获取 Bean 的实例,此时,如果是第一次获取某个 Bean,它就会被初始化。在此过程中,如果判断出一个 Bean 是工厂 Bean,那么还会做响应的处理。
其实在此过程中,由于这些 Bean 都是单例的,Spring 还会再这些 Bean 被实例化完成之后,将他们放到一个singletonObjects
成员变量中,可以通过getSingleton(String beanName)
方法从中获取 Bean 的实例。
这些 Bean 都被初始化完之后,还会对实现了 SmartInitializingSingleton 接口的实例,调用他们的afterSingletonsInstantiated
方法。这个接口只定义了这一个方法,目的是提供了一个扩展点,可以让 Bean 在被初始化之后执行一些操作。
后续
至此,初始化非懒加载 Bean 的过程也介绍完了。在refresh
中只剩下最后一个步骤,我们下一篇分析。
转载自:https://juejin.cn/post/7135993461504737317