Spring 源码阅读 17:初始化非懒加载的单例 Bean

这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 16:初始化事件广播器 & 注册事件监听器

前情提要

在之前的 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中只剩下最后一个步骤,我们下一篇分析。

猜你喜欢

转载自juejin.im/post/7135993461504737317