天天看點

萬字長文!帶你探索 Bean 加載流程

作者:Java架構學習指南
本文基于 Spring Boot 3.0.0 (Spring 6.0.2),Bean 指的是 Singleton Bean。

宏觀地說,Bean 加載流程大緻有三個階段,分别是執行個體化 createBeanInstance() 、屬性填充 populateBean() 和 初始化 initializeBean(),當 Bean 加載流程執行完畢,Bean 才具備使用條件!對 Bean 加載流程的探索是一段非常煎熬的旅程,你準備好了嗎?

萬字長文!帶你探索 Bean 加載流程

1 寫在前面

為了讓 Spring IoC 容器正确地完成 Bean 加載流程,必須要有一個資料模型來承載 Bean 的配置中繼資料,配置中繼資料可以告訴 Spring IoC 容器 如何建立 Bean 執行個體、何時建立 Bean 執行個體 和 Bean 的生命周期細節 等資訊,這個資料模型就是BeanDefinition接口,在其衆多實作類中,RootBeanDefinition 最為常用!

在 Spring Boot 中,BeanDefinition 可以歸納為兩類:一種是由開發人員聲明的,另一種是由官方及第三方起步依賴 starter 元件中聲明的,當然這兩種 BeanDefinition 一般都是通過注解聲明的。為了加載這兩種 BeanDefinition,Spring Boot 會率先完成一個名為 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 、ConfigurationClassPostProcessor類型的 Bean 加載流程。ConfigurationClassPostProcessor 實作了BeanDefinitionRegistryPostProcessor接口。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry);
}           

BeanDefinitionRegistryPostProcessor 繼承自BeanFactoryPostProcessor接口,後者是一個極其重要的 Spring IoC 拓展點。

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}           

BeanFactoryPostProcessor 常用于與 BeanDefinition 互動,而非與 Bean 執行個體互動,因為當執行到postProcessBeanFactory()方法時,隻是完成了所有 BeanDefinition 的加載而已,Bean 還沒有執行個體化呢。我們完全可以通過 BeanFactoryPostProcessor 将某一 BeanDefinition 執行個體的 beanClass 屬性值替換為CGLIB所生成的 Class 執行個體,這樣後期通過該 BeanDefinition 執行個體所生成的 Bean 執行個體就得到增強了。

當執行到 ConfigurationClassPostProcessor 中的postProcessBeanDefinitionRegistry()方法時,Spring Boot 已經提前将由@SpringBootApplication注解标注的啟動類注冊到了BeanDefinitionRegistry中去,那麼 ConfigurationClassPostProcessor 會根據這個啟動類來挖掘出所有的 BeanDefinition,接着将它們注冊到 BeanDefinitionRegistry 中。

為什麼一個由 @SpringBootApplication 注解标注的啟動類可以衍生出全部的 BeanDefinition 呢?因為它頭上有三個重要的元注解,分别是:@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。其中,@ComponentScan 注解用于衍生出 classpath 下特定包名内的 BeanDefinition,一般這個包名就是 Spring Boot 啟動類所在的包名,這也正是為什麼啟動類總是處于頂級包名下的答案;而 @EnableAutoConfiguration 注解則用于衍生出官方及第三方起步依賴元件中的 BeanDefinition,如果沒有這個注解,那麼一切官方及第三方 starter 元件都會趴窩。

繼續回到 ConfigurationClassPostProcessor 中來。限于篇幅這裡僅放出其核心外圍源碼,如下所示。其中,postProcessBeanDefinitionRegistry() 方法是先于 postProcessBeanFactory() 方法執行的。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
    /**
     * Derive further bean definitions from the configuration classes in the registry.
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        processConfigBeanDefinitions(registry);
    }

    /**
     * Prepare the Configuration classes for servicing bean requests at runtime
     * by replacing them with CGLIB-enhanced subclasses.
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        enhanceConfigurationClasses(beanFactory);
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }
}           

1.1 postProcessBeanDefinitionRegistry()

postProcessBeanDefinitionRegistry() 方法無疑是 ConfigurationClassPostProcessor 中最為核心的内容,它可以将那些由開發人員和起步依賴元件中所聲明的 BeanDefinition 全部加載出來,并将其注冊到 BeanDefinitionRegistry 中去。

Spring 提供了一個 ConfigurationClass類,用于抽象那些由 @Configuration 注解标記的配置類。metadata 屬性用于辨別配置類頭上的注解中繼資料;beanMethods 屬性用于收集配置類中由 @Bean 注解辨別的方法;importedBy 用于收集目前配置類是由哪些配置類通過 @Import 注解引入的;importedResources 屬性用于收集目前配置類通過 @ImportResource 注解所引入的包含若幹 Bean 定義資訊的 XML 檔案;importBeanDefinitionRegistrars 屬性用于收集目前配置類通過 @Import 注解引入的 ImportBeanDefinitionRegistrar。

public final class ConfigurationClass {
    private final AnnotationMetadata metadata;
    private final Resource resource;
    private String beanName;
    private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
    private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
    private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
            new LinkedHashMap<>();
    private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
            new LinkedHashMap<>();
}			           

第一個 ConfigurationClass 執行個體就是 Spring Boot 的啟動類!ConfigurationClassPostProcessor 會委托ConfigurationClassParser來解析 ConfigurationClass 執行個體以填充其屬性,而一個 ConfigurationClass 執行個體可能衍生出若幹個 ConfigurationClass 執行個體,最終這些 ConfigurationClass 執行個體會被儲存在 ConfigurationClassParser 中的成員變量 configurationClasses 中,然後 ConfigurationClassPostProcessor 會委派ConfigurationClassBeanDefinitionReader 負責将每一個 ConfigurationClass 執行個體中所蘊含的 BeanDefinition 加載出來。

相較于 BeanDefinitionReader 對 BeanDefinition 的加載,ConfigurationClassParser 對 ConfigurationClass 的解析更為吸引人,這一過程涉及對nested member class、@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean 和 default methods on interfaces 這七個目标的解析。

  • nested member class 指的是目前 ConfigurationClass 的嵌套成員靜态配置類,這些配置類的頭上如果有 @Component、@ComponentScan、@Import、和 @ImportResource 這四種注解任意一種标記,或者其包含由 @Bean 注解标記的方法,那麼 ConfigurationClassParser 會将其封裝為一個 ConfigurationClass 執行個體,然後對其進行遞歸解析。
  • @PropertySource 注解常用于引入一個或多個外部配置源檔案,ConfigurationClassParser 會将每一個外部配置源檔案封裝為一個PropertySource執行個體,然後直接将其塞進Environment中。
  • 對于 @ComponentScan 注解,ConfigurationClassParser 首先委派 ComponentScanAnnotationParser 進行掃描。具體地,ComponentScanAnnotationParser 會根據 @ComponentScan 注解中 basePackages 與 basePackageClasses 這倆屬性來探測出所要掃描的包名,如果沒有擷取到,就根據 @ComponentScan 注解所依附宿主類的包名作為掃描目标;然後将目标包下所有由 @Component 注解标注的類掃描出來并封裝為 BeanDefinition 執行個體,接着逐個将 BeanDefinition 執行個體注冊到 BeanDefinitionRegistry 中。
  • @Import 注解用于引入外部配置類,外部配置類可以是由 @Configuration 注解标記的正常配置類,也可以是 ImportSelector 和 ImportBeanDefinitionRegistrar 接口的實作類。processImports() 方法正是 ConfigurationClassParser 處理這三類目标的核心邏輯所在。其主要邏輯為:1) 如果引入的是純 ImportSelector,則遞歸調用 processImports() 方法;2) 如果引入的是 DeferredImportSelector,則延遲處理;3) 如果引入的是 ImportBeanDefinitionRegistrar,則填充目前 ConfigurationClass 執行個體中的 importBeanDefinitionRegistrars 屬性;4) 如果引入的是普通 @Configuration 配置類,則建構一個新的 ConfigurationClass 執行個體然後遞歸解析該 ConfigurationClass 執行個體。
  • @ImportResource 注解常用于引入一個或多個包含若幹 Bean 定義資訊的 XML 檔案。一般大家是用不到這個注解的,但有些老的子產品可能依然是通過 XML 檔案來聲明 Bean 定義資訊,那麼 @ImportResource 注解恰好提供了這種相容能力。ConfigurationClassParser 正是在這裡填充目前 ConfigurationClass 執行個體的 importedResources 屬性。
  • 對于方法級的 @Bean 注解,ConfigurationClassParser 會為其封裝為 BeanMethod 執行個體,然後将其回填到目前 ConfigurationClass 執行個體的 beanMethods 屬性中。
  • default methods on interfaces 指的是 Java 8 接口中由default關鍵字修飾的預設方法。ConfigurationClassParser 會擷取目前 ConfigurationClass 所實作的接口,然後探測出接口中所有 @Bean 方法,依然是填充目前 ConfigurationClass 執行個體的 beanMethods 屬性。

1.2 postProcessBeanFactory()

當執行到 postProcessBeanFactory() 方法,這意味着所有的 BeanDefinition 已經完成了加載,它們都安靜地躺在 BeanDefinitionRegistry 中。 而enhanceConfigurationClasses()方法是 postProcessBeanFactory() 方法中的唯一邏輯,咱們一起來瞅瞅它幹了啥吧。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
    public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
            Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
            if ("full".equals(configClassAttr)) {
                configBeanDefs.put(beanName, abd);
            }
        }
        if (configBeanDefs.isEmpty()) {
            return;
        }
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
            AbstractBeanDefinition beanDef = entry.getValue();
            // Set enhanced subclass of the user-specified bean class
            Class<?> configClass = beanDef.getBeanClass();
            Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
            if (configClass != enhancedClass) {
                beanDef.setBeanClass(enhancedClass);
            }
        }
    }
}           

代碼不多,了解起來很容易。周遊所有 BeanDefinition 執行個體,将所有包含 key 為 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE、value 為 full 的 BeanDefinition 執行個體 收集到局部變量 configBeanDefs 中,然後使用 CGLIB 生成全新的 Class 執行個體并替換 BeanDefinition 執行個體的 beanClass 屬性值,後續這些 Bean 均将會被代理類增強。

以 key 為 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE、value 為 full 的鍵值對兒是啥時候塞到 BeanDefinition 執行個體的 attribute 中去的呢?當然是在 postProcessBeanDefinitionRegistry() 方法中塞進去的,主要内容如下。

public abstract class ConfigurationClassUtils {
    public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        String className = beanDef.getBeanClassName();
        if (className == null || beanDef.getFactoryMethodName() != null) {
            return false;
        }
        AnnotationMetadata metadata;
        if (beanDef instanceof AnnotatedBeanDefinition
                && className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
            metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
        } else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
            Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
            if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
                    BeanPostProcessor.class.isAssignableFrom(beanClass) ||
                    AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
                    EventListenerFactory.class.isAssignableFrom(beanClass)) {
                return false;
            }
            metadata = AnnotationMetadata.introspect(beanClass);
        } else {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
            metadata = metadataReader.getAnnotationMetadata();
        }
        Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
        if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
        } else if (config != null || isConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
        } else {
            return false;
        }
        Integer order = getOrder(metadata);
        if (order != null) {
            beanDef.setAttribute(ORDER_ATTRIBUTE, order);
        }
        return true;
    }
}           

忽略 根據 BeanDefinition 擷取其 AnnotationMetadata 的邏輯,從上面代碼中,我們可以很輕松地總結出以下規律:

  1. 若 @Configuration 注解中 proxyBeanMethods 屬性值為 true,則執行 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
  2. 若 @Configuration 注解中 proxyBeanMethods 屬性值為 false,則執行 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "lite")。
經過第一章節的分析,我們大緻了解了 BeanDefinition 的加載邏輯,接下來将會面臨更大的挑戰,就是對 Bean 加載流程的探索。

2 執行個體化

BeanFactory是 Spring 針對 IoC 容器提供的最頂級抽象接口,其主要用于加載 Bean。衆所周知,相較于 BeanFactory,ApplicationContext擁有更多的拓展功能,是以後者在企業級應用中更為常用,但今天筆者想給大家介紹 BeanFactory 的另一個拓展,即AutowireCapableBeanFactory。AutowireCapableBeanFactory 提供了兩個重要的方法,分别是:createBean()和autowireBean()。createBean() 方法可以用于建構一個不受 IoC 容器納管的 Bean 執行個體,而且在執行個體化 Bean 之後,也會進行 populateBean() 和 initializeBean() 操作;而 autowireBean() 方法就更為有用了,它可以為那些頭上沒有 stereotype 注解的普通類注入 IoC 容器中的單例 Bean,這很酷對不對?

回歸正題!Bean 加載的統一入口位于AbstractBeanFactory中的getBean()方法處,熟悉 Spring 的朋友應該都知道:真正幹活的往往是那些 doXXX 開頭的方法。這裡也不例外,doGetBean()方法源碼内容如下。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, false);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, false);
    }

    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }

    protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) throws BeansException {
        // § 2.1
        String beanName = transformedBeanName(name);
        Object beanInstance;

        // § 2.2
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            // § 2.3
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        } else {
            // § 2.4
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory abf) {
                    return abf.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);
                }
            }

            // § 2.5
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

                // § 2.6
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        } catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

                // § 2.7
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        } catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
            } catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }
        
        // § 2.8
        return adaptBeanInstance(name, beanInstance, requiredType);
    }
}           
代碼很多,筆者已經在核心内容上打了标簽 ( § 2.1 - § 2.8 ),下面就以這些标簽作為獨立的小節,逐一分析這些核心内容。

§ 2.1 轉換 beanName

transformedBeanName() 方法會對傳進來的 beanName 進行轉換。如果傳進來的 beanName 以 & 為字首,那麼這裡會移除該字首;如果傳進來的 beanName 是某一個 Bean 的 aliasName,那麼找到該 aliasName 真正所指向的 beanName。

一般情況下,Spring 結合 BeanDefinition 執行個體中的 beanClass 屬性與反射機制來執行個體化 Bean。但某些情況下,執行個體化 Bean 的過程比較複雜,直接通過反射建構出來的執行個體沒有太多意義。Spring 為此提供了一個可以靈活配置執行個體化 Bean 的方案,這一方案隐藏了執行個體化複雜 Bean 的細節,它就是大名鼎鼎的FactoryBean接口。getObject() 方法用于傳回目前 FactoryBean 所建立的複雜 Bean 的執行個體。

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    default boolean isSingleton() { return true; }
}           

Spring 自身針對 FactoryBean 接口就提供了多達 50+ 種實作類,咱們拿Jackson2ObjectMapperFactoryBean這個實作類來展開講講 FactoryBean 的應用場景。ObjectMapper是 Jackson 暴露給開發人員使用的核心序列化 API,在使用 ObjectMapper 之前,往往需要一些配置,畢竟一個裸的 ObjectMapper 執行個體很可能會有一些坑,更何況 Spring 對定制化配置 ObjectMapper 的訴求更為強烈,是以我們可以認為 ObjectMapper 這個 Bean 的執行個體化是複雜的。大家可以自行閱讀 Jackson2ObjectMapperFactoryBean 類的源碼,還是蠻清晰的。

§ 2.2 嘗試從多級緩存中加載 Bean 執行個體

getSingleton(beanName) 方法會嘗試從多級緩存中加載 Bean。緩存共計三級,分别是一級緩存singletonObjects、二級緩存earlySingletonObjects和三級緩存singletonFactories。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    // First cache.
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    // Second cache.
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    // Third cache.
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    // Names of beans that are currently in creation.
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }
}           

上述源碼涉及三種 Map 類型的多級緩存,的确有些讓人崩潰 (後面會解答),但總體邏輯還是清晰的。

  1. 首先從一級緩存 singletonObjects 中擷取 Bean 執行個體,如果擷取到了,則直接傳回。
  2. 如果從一級緩存中未擷取到且目前 Bean 正處于加載流程中,然後從二級緩存 earlySingletonObjects 裡面擷取,如果擷取到了,則直接傳回。
  3. 如果從二級緩存中還是沒有擷取到的話,那麼從三級緩存 singletonFactory 裡擷取與 beanName 比對的 ObjectFactory,接着調用 ObjectFactory 的getObject()方法來擷取提前暴露的 Bean 執行個體 ( 如果需要為其生成代理,那此時還會為原始 Bean 執行個體生成代理類;此外,此時的 Bean 執行個體是一種早期狀态,僅僅是執行個體化 createBeanInstance() 了而已,還未開始執行屬性填充 populateBean() 和初始化 initializeBean() 這倆流程 ) ,最後将這一提前暴露的 Bean 執行個體放到二級緩存中,同時删除三級緩存中的内容。

當一個 Bean 執行個體被塞進一級緩存中,這意味着該 Bean 已經完成了加載流程,即走完了執行個體化 createBeanInstance()、屬性填充 populateBean() 和初始化 initializeBean() 流程,真正具備使用的條件了;此外,一級緩存也是確定單例 Bean 在 Spring IoC 容器中唯一性的關鍵,是以一級緩存中的内容是不會删除的。

§ 2.3 Bean 執行個體是否是最終想要的

不論 Bean 執行個體是直接從多級緩存中拿到的,還是直接建立的,并不一定就是我們想要的。是以,在拿到 Bean 執行個體之後,第一件事就是執行getObjectFromFactoryBean()方法。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
        // String FACTORY_BEAN_PREFIX = "&"
        // name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            // name 是未經 transformedBeanName() 方法轉換過的
            // 如果 name 以 & 為字首,但 Bean 執行個體卻又不是 FactoryBean 類型的,那隻能抛出異常了
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
            }
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }
            // name 以 & 為字首,說明目前 FactoryBean 類型的 Bean 執行個體就是最終想要的,直接傳回即可
            return beanInstance;
        }

        // name 不以 & 為字首
        // 如果目前 Bean 執行個體不是 FactoryBean 類型的,則直接傳回該 Bean 執行個體
        if (!(beanInstance instanceof FactoryBean<?> factoryBean)) {
            return beanInstance;
        }

        // 執行到這裡,說明 name 不以 & 為字首,但目前 Bean 執行個體是 FactoryBean 類型的
        // 進一步說明:FactoryBean#getObject() 傳回的 Bean 執行個體才是最終想要的
        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        if (object == null) {
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            // 核心邏輯
            object = getObjectFromFactoryBean(factoryBean, beanName, !synthetic);
        }
        return object;
    }
}           

getObjectFromFactoryBean() 方法主要邏輯有:

  1. 如果 name 以 & 為字首且 Bean 執行個體是 BeanFactory 類型的,那目前 Bean 執行個體就是最終想要的。
  2. 如果 name 不以 & 為字首且 Bean 執行個體不是 BeanFactory 類型的,那目前 Bean 執行個體就是最終想要的。
  3. 如果 name 不以 & 為字首但 Bean 執行個體是 BeanFactory 類型的,那目前 Bean 執行個體并不是最終想要的,故而委派 getObjectFromFactoryBean() 方法來比對最終想要的 Bean 執行個體。

很遺憾,當繼續跟進到 getObjectFromFactoryBean() 方法中後,并沒有我們想要看到的内容,但潛意識覺得 doGetObjectFromFactoryBean() 方法就是答案了,果然沒讓我們失望!

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
    private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
        Object object;
        try {
            object = factory.getObject();
        } catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }
        return object;
    }
}
複制代碼           

§ 2.4 嘗試從 parentBeanFactory 中加載 Bean

既然從多級緩存中沒有擷取到 Bean 執行個體,那隻能從頭開始、完整地走一遍 Bean 加載流程了,但 Spring 好像還不死心,想要試圖從parentBeanFactory拿到。如果 parentBeanFactory 存在,則遞歸調用 getBean() 方法。

§ 2.5 提前辨別目前 Bean 加載流程已完結

當程式執行到這裡,說明确實要從頭開始、完整地走一遍 Bean 加載流程,markBeanAsCreated()方法用于提前辨別目前 Bean 加載流程已完結,畢竟接下來肯定要真正觸發 Bean 加載流程的。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    // Map from bean name to merged RootBeanDefinition.
    private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
    // Names of beans that have already been created at least once.
    private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

    protected void markBeanAsCreated(String beanName) {
        if (!this.alreadyCreated.contains(beanName)) {
            synchronized (this.mergedBeanDefinitions) {
                this.alreadyCreated.add(beanName);
            }
        }
    }
}           

§ 2.6 優先加載由 @DependsOn 注解辨別的依賴 Bean

如果對 Bean A 的加載需要確定另一個 Bean B 已經加載完畢,而且 Bean B 并不是 Bean A 中所持有的成員變量,那就可以使用 @DependsOn 注解來滿足這一需求。

如果目前 BeanDefinition 的 dependsOn 屬性值非空,那麼就要優先加載 dependsOn 屬性中指定的 Bean 了。首先,委派registerDependentBean()方法來雙向記錄依賴關系;然後遞歸調用 getBean() 方法。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    // Map between dependent bean names: bean name to Set of dependent bean names.
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    // Map between depending bean names: bean name to Set of bean names for the bean's dependencies.
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

    public void registerDependentBean(String beanName, String dependentBeanName) {
        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans =
                    this.dependentBeanMap.computeIfAbsent(beanName, k -> new LinkedHashSet<>(8));
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }

        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean =
                    this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(beanName);
        }
    }
}
複制代碼           

§ 2.7 真正開始建立 Bean 執行個體

getSingleton(beanName, () -> createBean(beanName, mbd, args)) 真正拉開了 Bean 加載流程的序幕,但 屬性填充 populateBean() 和 初始化 initializeBean() 這兩塊内容放在第三章節與第四章節中講解。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    // Cache of singleton objects: bean name to bean instance.
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // Cache of early singleton objects: bean name to bean instance.
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    // Cache of singleton factories: bean name to ObjectFactory.
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    // Names of beans that are currently in creation.
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    // getSingleton(beanName, () -> createBean(beanName, mbd, args))
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 	this.singletonsCurrentlyInCreation.add(beanName)
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                try {
                    // createBean(beanName, mbd, args))
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } catch (BeanCreationException ex) {
                    throw ex;
                } finally {
                    // 	this.singletonsCurrentlyInCreation.remove(beanName)
                    afterSingletonCreation(beanName);
                }

                if (newSingleton) {
                    //	this.singletonObjects.put(beanName, singletonObject);
                    //	this.singletonFactories.remove(beanName);
                    //	this.earlySingletonObjects.remove(beanName);
                    addSingleton(beanName, singletonObject);
                }
            }

            return singletonObject;
        }
    }
}           

從上述代碼我們可以總結出建立 Bean 執行個體的核心外圍邏輯。

  1. 首先,向 singletonsCurrentlyInCreation 成員變量中插入 beanName,辨別該 Bean 正處于加載流程中。
  2. 其次,回調ObjectFactory的getObject()方法,getObject() 方法中的全部邏輯就是調用 createBean() 方法,createBean() 方法涵蓋了完整的 Bean 加載流程。
  3. 再次,此時 Bean 執行個體不僅建立完畢,而且還完成了屬性填充 populateBean() 和 初始化 initializeBean() 流程,真正具備使用的條件了,那麼自然要從 singletonsCurrentlyInCreation 中删除該 beanName,辨別該 Bean 加載流程走完了。
  4. 最後,将 Bean 執行個體追加到一級緩存 singletonObjects 中,同時删除二級緩存 earlySingletonObjects 和三級緩存 singletonFactories 中的内容。
接下來,終于要到 createBean() 方法中去探尋真理了。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
    /**
     * Central method of this class: creates a bean instance,
     * populates the bean instance, applies post-processors, etc.
     */
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
        RootBeanDefinition mbdToUse = mbd;
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        } catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            return beanInstance;
        } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            throw ex;
        } catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }
}           

顯然,從上述 createBean() 方法内容來看,doCreateBean()方法才有我們想要的真理!但在執行 doCreateBean() 方法前有兩個知識點需要大家去關注。

  1. 如果目前 BeanDefinition 執行個體的 beanClass 屬性值是字元串類型的,那麼 resolveBeanClass() 方法會根據字元串類型的 beanClass 解析出一個 Class 執行個體,然後替換更新 beanClass 屬性值。這其中涉及的關鍵技術是 Spring EL。
public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(MumuApplication.class, args);
    AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
    StandardBeanExpressionResolver beanExpressionResolver =
            new StandardBeanExpressionResolver(Thread.currentThread().getContextClassLoader());
    Object restTemplateBuilderClazz =
            beanExpressionResolver.evaluate("#{restTemplateBuilder.class}",
                    new BeanExpressionContext((ConfigurableBeanFactory) autowireCapableBeanFactory, null));
    Object restTemplateBuilderBean =
            beanExpressionResolver.evaluate("#{restTemplateBuilder}",
                    new BeanExpressionContext((ConfigurableBeanFactory) autowireCapableBeanFactory, null));
}           
  1. 大家應該清楚 Spring AOP 是通過BeanPostProcessor将原生 Bean 執行個體替換為代理對象的。而 resolveBeforeInstantiation() 方法同樣是利用 BeanPostProcessor 來生成代理,但這裡代理生成的時機更為靠前,因為原生 Bean 執行個體都還沒有生成呢。卧槽,這一點我還真不知道!
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    // 生成代理核心邏輯
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
            if (result != null) {
                return result;
            }
        }
        return null;
    }
}           

從 resolveBeforeInstantiation() 方法最終跟蹤到了AbstractAutoProxyCreator類中,它當然是一個 BeanPostProcessor 實作類,主要内容如下。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }
}           

從 AbstractAutoProxyCreator 關于代理的生成政策來看:如果基于 beanClass 可以擷取到一個 TargetSource 執行個體,那麼就将建立一個代理類。但筆者一路 DEBUG 下去,也沒發現有代理類是在這裡建立的,是以這裡不做過多展開了,真不太懂這玩意兒呢。

好了!這兩個需要大家關注的知識點講完了,接下來就要專心攻堅 doCreateBean() 這家夥了。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
        Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                } catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.markAsPostProcessed();
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
                throw bce;
            } else {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
            }
        }

        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                        StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                        "] in its raw version as part of a circular reference, but has eventually been " +
                                        "wrapped. This means that said other beans do not use the final version of the " +
                                        "bean. This is often the result of over-eager type matching - consider using " +
                                        "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }
}           

為了加深印象,這裡貼一張 doCreateBean() 方法的 UML 時序圖,如下所示。

萬字長文!帶你探索 Bean 加載流程

在上述 doCreateBean() 方法的源碼中,我們終于看到了 createBeanInstance()、populateBean() 和 initializeBean() 的身影。剛剛說了,populateBean() 和 initializeBean() 這倆方法在後續章節分析,而筆者也不打算分析 createBeanInstance() 方法的内容,這個内容真是晦澀難懂啊,記住 “Bean 執行個體主要是通過反射機制來建構” 這一點就行了。

在 createBeanInstance() 方法執行完之後,緊接着執行applyMergedBeanDefinitionPostProcessors()方法,主要是周遊MergedBeanDefinitionPostProcessor并調用其postProcessMergedBeanDefinition()方法。MergedBeanDefinitionPostProcessor 的應用時機介于 執行個體化 createBeanInstance() 與 屬性填充 populateBean() 之間,為後續 屬性填充 populateBean() 和 初始化 initializeBean() 階段提前緩存LifecycleMetadata和InjectionMetadata。MergedBeanDefinitionPostProcessor 接口有三個重要的實作類,如下所示。

萬字長文!帶你探索 Bean 加載流程
  • 在 InitDestroyAnnotationBeanPostProcessor 中,其 postProcessMergedBeanDefinition() 方法負責将由 @PostConstruct、@PreDestroy 注解修飾的方法封裝為 LifecycleMetadata 執行個體,然後将其緩存到該類的成員變量lifecycleMetadataCache中。
  • 在 CommonAnnotationBeanPostProcessor 中,其 postProcessMergedBeanDefinition() 方法負責将由 @Resource 注解修飾的變量或方法封裝為 InjectionMetadata 執行個體,然後将其緩存到該類的成員變量injectionMetadataCache中。
  • 在 AutowiredAnnotationBeanPostProcessor 中,其 postProcessMergedBeanDefinition() 方法負責将由 @Autowired、@Value、@Inject 注解修飾的變量或方法封裝為 InjectionMetadata 執行個體,然後将其緩存到該類的成員變量injectionMetadataCache中。
下面開始剖析 Spring 是如何解決循環依賴這一問題的!Spring Boot 自 2.6.0 版本開始預設不允許循環依賴,需要通過spring.main.allow-circular-references配置項手動開啟該功能。

首先,要搞清楚啥是循環依賴。循環依賴就是循環引用,比如 Bean A 引用了 Bean B,而 Bean B 又引用了 Bean A,最終導緻二者之間形成了一個引用環。如果引用關系是通過構造方法來搭建的,那麼這種循環依賴是無解的,但如果引用關系是通過 setter 方法來搭建的,那麼 Spring 是完全有能力破局的。循環依賴可以分為下圖中的這兩種類型,其他情形的循環依賴都是它們的變體而已。

萬字長文!帶你探索 Bean 加載流程

為了解決循環依賴問題,Spring 的政策是不等 Bean 加載流程全部完成,在執行個體化完成之後就将 Bean 執行個體提前暴露出來,它是如何暴露的呢?

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}           

可以發現,提前暴露 Bean 執行個體是通過addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))這一行代碼實作的。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}           

最終,addSingletonFactory()方法通過getEarlyBeanReference(beanName, mbd, bean)方法來将該 Bean 執行個體包裝為一個ObjectFactory,然後丢到三級緩存 singletonFactories 中。

看到這裡,相信大家肯定會有一個疑惑:明明 Bean 執行個體已經生成了,它就在那裡,為啥還要通過 getEarlyBeanReference(beanName, mbd, bean) 方法來擷取 Bean 執行個體呢,這不是“脫褲子放屁,多此一舉”嗎?

為了解答這一疑惑,我們先來看一下 getEarlyBeanReference(beanName, mbd, bean) 方法都幹了啥吧。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}           

繼續跟蹤到AbstractAutoProxyCreator中的 getEarlyBeanReference() 方法中。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
}           

看到wrapIfNecessary()方法就應該明白了!wrapIfNecessary() 方法負責探測是否需要為某一 Bean 建立代理,如果需要就将為其建立代理。但代理類的建立多發生于初始化階段,這裡為啥将這一動作提前了呢?當然,如果 getEarlyBeanReference(beanName, mbd, bean) 确實為某一 Bean 生成了代理類,後續初始化階段也不會重新生成代理類,因為在 AbstractAutoProxyCreator 中有一個成員變量earlyProxyReferences可以辨別這一狀态。

現在,我們隻需要搞清楚一個問題就行了,那就是 getEarlyBeanReference(beanName, mbd, bean) 方法為什麼會如此着急地建立代理?細細想來,這麼做絕對是正确的。假設 Bean A 與 Bean B 構成循環依賴關系,而且 Bean A 是需要 Spring AOP 為其生成代理的,也就是說最終駐留在 IoC 容器中的 Bean A 是一個代理。

  1. 嘗試從多級緩存中加載 Bean A,此時肯定是加載不到的,必須要完整走一遍 Bean 加載流程了。
  2. createBeanInstance() 方法為 Bean A 生成原生的 Bean 執行個體。
  3. 将 Bean A 的執行個體提前暴露到三級緩存 singletonObjects 中,此時由于需要為 Bean A 生成代理,那麼最終躺在三級緩存 singletonObjects 中的是其代理。
  4. populateBean() 方法為 Bean A 進行屬性填充,這時發現需要為 Bean A 注入 Bean B。
  5. 嘗試從多級緩存中加載 Bean B,此時肯定是加載不到的,必須要完整走一遍 Bean 加載流程了。
  6. createBeanInstance() 方法為 Bean B 生成 Bean 執行個體。
  7. 将 Bean B 的執行個體提前暴露到三級緩存 singletonObjects 中。
  8. populateBean() 方法為 Bean B 進行屬性填充,這時發現需要為 Bean B 注入 Bean A,自然會觸發對 Bean A 的加載流程。
  9. 這個時候已經可以從多級緩存中加載到 Bean A 了,因為它此刻正躺在三級緩存 singletonObjects 中呢。
  10. populateBean() 方法會觸發 getBean(Bean A) 的加載流程,既然直接從多級緩存中拿到了 Bean A,接下來,自然是繼續為 Bean B 注入 Bean A 了。
想象一下,如果 getEarlyBeanReference(beanName, mbd, bean) 方法沒有将建立代理的動作提前,那麼此時此刻注入到 Bean B 中的 Bean A 隻是其原生的執行個體,而不再是代理!

最後,再看一個關于多級緩存與循環依賴的知識點,内容如下所示,依然是 doCreateBean() 方法中的内容。

Object exposedObject = bean;
try {
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}

if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                        "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
            }
        }
    }
}           

筆者在看完上述内容後,也是思考了許久才搞明白。主要不了解actualDependentBeans非空的話,為啥就需要抛出異常。後來結合那一長坨異常資訊才頓悟了,有點無語,哈哈。首先,通過 getSingleton(beanName, false)方法從多級緩存中加載 Bean,注意傳入的 allowEarlyReference 參數值為 false,也就是說隻會嘗試從一級緩存 singletonObjects 和 二級緩存 earlySingletonObjects 中加載 Bean 了。

如果 getSingleton(beanName, false) 方法加載到的 Bean 是來自于二級緩存 earlySingletonObjects 的,那說明什麼問題?記住:一旦某一 Bean 的早期執行個體從三級緩存 singletonFactories 轉移到了二級緩存 earlySingletonObjects 中,那麼該 Bean 一定是與另一個 Bean 構成了循環依賴,而且轉移的時機就是另一個與其循環依賴的 Bean 将其作為依賴注入進去了;換句話說,在循環依賴場景下,二級緩存 earlySingletonObjects 是用來辨別某一早期 Bean 執行個體是否被别人拿去注入了。

有了這些知識的鋪墊,了解上述内容就容易很多了。如果 getSingleton(beanName, false) 方法傳回的 earlySingletonReference 非空且 earlySingletonReference 與 exposedObject 不一緻,那說明 initializeBean() 方法生成了代理 Bean,而此時 earlySingletonReference 這個早前 Bean 執行個體已經被其他 Bean 使用 (注入) 了,這時 actualDependentBeans 肯定是非空的,當然要抛出異常了!

§ 2.8 轉換 Bean 執行個體為特定類型

Bean 的加載流程已經接近尾聲了,還有最後一個步驟,就是對 Bean 進行類型轉換。大多數情況下,adaptBeanInstance() 方法中 requiredType 這一參數值是空的,也就是毛都不做;但如果 requiredType 非空,那就需要将 Bean 執行個體轉換為特定的類型。

public abstract class AbstractBeanFactory {
    public <T> T adaptBeanInstance(String name, Object bean, Class<?> requiredType) {
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return (T) convertedBean;
            } catch (TypeMismatchException ex) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }

        return (T) bean;
    }
}           
執行個體化的邏輯難就難在循環依賴這一塊,除此之外也沒啥别的幹貨了,Bean 加載流程中的幹貨基本都是在屬性填充與初始化這倆環節。

3 屬性填充

萬字長文!帶你探索 Bean 加載流程

關于屬性填充或者屬性注入的概念,大家或多或少都了解一些,下面就一起來看看它的全部邏輯吧。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
    /**
     * Populate the bean instance in the given BeanWrapper with the property values
     * from the bean definition.
     */
    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        // § 3.1
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }


        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // Add property values based on autowire by name if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                // § 3.2
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                // § 3.3
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

        // § 3.4
        if (hasInstantiationAwareBeanPostProcessors()) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
                pvs = pvsToUse;
            }
        }

        // § 3.5
        if (pvs != null) {
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }
}           

§ 3.1 postProcessAfterInstantiation()

Spring 針對 Bean 執行個體化之後、屬性填充之前的這一階段,通過InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation()方法提供了一個拓展點,postProcessAfterInstantiation() 方法傳回 false,那麼後續将不再進行屬性填充操作。目前,筆者還沒有發現哪裡使用到了這一拓展點,大家了解即可。

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    /**
     * Perform operations after the bean has been instantiated, via a constructor or factory method,
     * but before Spring property population (from explicit properties or autowiring) occurs.
     * <p>The default implementation returns {@code true}.
     * @param bean the bean instance created, with properties not having been set yet
     * @param beanName the name of the bean
     * @return {@code true} if properties should be set on the bean; {@code false}
     * if property population should be skipped. Normal implementations should return {@code true}.
     */
    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }
}           

§ 3.2 autowireByName()

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
    protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            // 	public boolean containsBean(String name) {
            //		String beanName = transformedBeanName(name);
            //		if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
            //			return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
            //		}
            //		return false
            //	}
            //  -------------------------------------------------------------------------
            // 	public boolean containsSingleton(String beanName) {
            //		return this.singletonObjects.containsKey(beanName);
            //	}
            //  -------------------------------------------------------------------------
            // 	public boolean containsBeanDefinition(String beanName) {
            //		Assert.notNull(beanName, "Bean name must not be null");
            //		return this.beanDefinitionMap.containsKey(beanName);
            //	}
            if (containsBean(propertyName)) {
                Object bean = getBean(propertyName);
                pvs.add(propertyName, bean);
                registerDependentBean(propertyName, beanName);
            }
        }
    }

    protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
        Set<String> result = new TreeSet<>();
        PropertyValues pvs = mbd.getPropertyValues();
        PropertyDescriptor[] pds = bw.getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            if (pd.getWriteMethod() != null && !pvs.contains(pd.getName()) &&
                    !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
                result.add(pd.getName());
            }
        }
        return StringUtils.toStringArray(result);
    }
}           

每個 RootBeanDefinition 執行個體持有一MutablePropertyValues類型的成員變量,該成員變量持有若幹個PropertyValue執行個體,而一個 PropertyValue 執行個體則代表着一對 key/value 鍵值對兒。autowireByName()方法的核心邏輯就是向目前 RootBeanDefinition 執行個體所持有的 MutablePropertyValues 變量内插入一個或多個 PropertyValue 執行個體,此時 PropertyValue 執行個體的 key 是目前 Bean 執行個體中某一成員變量名,value 則是 IoC 容器中一個以 key 為 beanName 的 Bean 執行個體,如果該 Bean 尚未加載,那麼此時會進行 Bean 加載。

一個 Bean 執行個體中會有多個成員變量,那麼究竟哪些成員變量才有資格被封裝成一個 PropertyValue 執行個體、最終進入到 MutablePropertyValues 中呢?

  1. 首先,在一級緩存 singletonObjects 與 beanDefinitionMap 二者中至少有一個能比對到該 beanName。
  2. 其次,該成員變量必須要有相應的 setter 方法;由 @Autowired 或 @Resource 注解标注的成員變量一般是過不了這一關的,因為咱們平時很少為它們編寫 setter 方法。
  3. 再次,該成員變量名稱在目前 RootBeanDefinition 執行個體所持有的成員變量 MutablePropertyValues 中必須是不存在。
  4. 最後,該成員變量的類型不能是一種Simple Property,具體參考下圖。
萬字長文!帶你探索 Bean 加載流程

§ 3.3 autowireByType()

autowireByType() 方法與 autowireByName() 方法的核心邏輯是一緻的,首先将目前 Bean 執行個體中符合條件的成員變量封裝為一個 PropertyValue 執行個體,然後把這些 PropertyValue 執行個體塞進相應 RootBeanDefinition 執行個體的成員變量 MutablePropertyValues 中去。但相較于 autowireByName() 方法,autowireByType() 方法内容卻複雜得多,這是為什麼呢?在上一小節中曾提到:PropertyValue 執行個體所持有的 value 是一個已完成加載流程的 Bean 執行個體。在 byName 場景下,這麼說是沒問題的,因為在 IoC 容器中 beanName 與 Bean 執行個體是一一對應的;但在 byType 場景下可就不一定了,因為在 IoC 容器中 beanType 與 Bean 執行個體可以是一對多的關系!

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            try {
                PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
                if (Object.class != pd.getPropertyType()) {
                    MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                    boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
                    DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                    // 核心邏輯
                    Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                    if (autowiredArgument != null) {
                        // 最終目的
                        pvs.add(propertyName, autowiredArgument);
                    }
                    for (String autowiredBeanName : autowiredBeanNames) {
                        registerDependentBean(autowiredBeanName, beanName);
                    }
                    autowiredBeanNames.clear();
                }
            } catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
            }
        }
    }
}           

大家應該可以很自然地想到resolveDependency()方法所解析出的結果可能是一個已完成加載流程的 Bean 執行個體,也可能是一個 Array、Collection或者Map類型、包含多個同一 beanType 且已完成加載流程的 Bean 執行個體。為什麼這麼肯定 Bean 執行個體一定是已完成加載流程的呢?因為 resolveDependency() 方法内部最終也是委派 AbstractBeanFactory 的 getBean(beanName) 方法來拿到想要的單例 Bean。

resolveDependency()方法絕對是重中之重,但源碼太長了,一層套一層的,這裡僅貼出它所極度依賴的findAutowireCandidates()方法吧。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    /**
     * Find bean instances that match the required type.
     * Called during autowiring for the specified bean.
     */
    protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
        // 核心邏輯
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
        for (String candidate : candidateNames) {
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                // 核心邏輯
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        return result;
    }

    private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
                                   DependencyDescriptor descriptor, Class<?> requiredType) {
        if (descriptor instanceof MultiElementDescriptor) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            if (!(beanInstance instanceof NullBean)) {
                candidates.put(candidateName, beanInstance);
            }
        } else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor streamDescriptor && streamDescriptor.isOrdered())) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
        } else {
            candidates.put(candidateName, getType(candidateName));
        }
    }
}

public class DependencyDescriptor extends InjectionPoint implements Serializable {
    public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
            throws BeansException {
        return beanFactory.getBean(beanName);
    }
}           

§ 3.4 postProcessProperties()

postProcessProperties()方法由InstantiationAwareBeanPostProcessor接口聲明,是一種面向注解的依賴注入拓展點。Spring 為其提供了兩個極其重要的實作類,如下圖所示。

萬字長文!帶你探索 Bean 加載流程

3.4.1 CommonAnnotationBeanPostProcessor

在CommonAnnotationBeanPostProcessor中,postProcessProperties() 方法負責為 Bean 執行個體内由@Resource注解辨別的成員變量注入依賴。

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
        implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
        }
        return pvs;
    }
}           
public class InjectionMetadata {
    public void inject(Object target, String beanName,PropertyValues pvs) throws Throwable {
        Collection<InjectedElement> checkedElements = this.checkedElements;
        Collection<InjectedElement> elementsToIterate =
                (checkedElements != null ? checkedElements : this.injectedElements);
        if (!elementsToIterate.isEmpty()) {
            for (InjectedElement element : elementsToIterate) {
                element.inject(target, beanName, pvs);
            }
        }
    }

    protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
            throws Throwable {
        if (this.isField) {
            Field field = (Field) this.member;
            ReflectionUtils.makeAccessible(field);
            field.set(target, getResourceToInject(target, requestingBeanName));
        } else {
            try {
                Method method = (Method) this.member;
                ReflectionUtils.makeAccessible(method);
                method.invoke(target, getResourceToInject(target, requestingBeanName));
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }
}           

從InjectionMetadata源碼内容可以明顯看出最終是通過反射機制來實作依賴注入的!

3.4.2 AutowiredAnnotationBeanPostProcessor

在AutowiredAnnotationBeanPostProcessor中,postProcessProperties() 方法負責為 Bean 執行個體内由@Autowired、@Value和@Inject注解辨別的成員變量注入依賴。

public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
        MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware {
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        } catch (BeanCreationException ex) {
            throw ex;
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }
}           

從上面源碼内容來看,CommonAnnotationBeanPostProcessor 與 AutowiredAnnotationBeanPostProcessor 最終都是以 InjectionMetadata 進行依賴注入,但各自findAutowiringMetadata()方法中的邏輯是不一樣的,因為面向的是不同類型的注解。此外,有必要提一下@Value注解的解析邏輯,解析入口位于ConfigurableBeanFactory接口中的resolveEmbeddedValue()方法,其内部會委派StringValueResolver去解析,PlaceholderResolvingStringValueResolver是 StringValueResolver 接口一個比較重要的實作類,@Value注解的解析邏輯最終就在該實作類中!

§ 3.5 applyPropertyValues()

程式運作到這裡,目前 Bean 執行個體内由@Resource、@Autowired、@Value和@Inject注解辨別的成員變量已經全部完成了依賴注入,但目前 RootBeanDefinition 執行個體内 MutablePropertyValues 類型的成員變量中還有若幹 PropertyValue 執行個體沒有被當成依賴而注入到目前 Bean 執行個體中去,這些 PropertyValue 執行個體主要是由autowireByName()和autowireByType()方法塞進來的,當然也可以由開發人員自定義的BeanFactoryPostProcessor實作類來置入。

最終也是通過反射機制為目前 Bean 執行個體實作依賴注入的,如下圖所示。

萬字長文!帶你探索 Bean 加載流程

4 初始化

謝天謝地!我們終于來到了較為輕松的初始化階段。對@PostConstruct、InitializingBean和Custom Init Method這三種初始化方法的調用絕不僅僅是初始化階段的全部,事實上還有更為重要的知識點值得我們去關注。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    /**
     * Initialize the given bean instance, applying factory callbacks
     * as well as init methods and bean post processors.
     */
    protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
        // § 4.1
        invokeAwareMethods(beanName, bean);

        // § 4.2
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        // § 4.3
        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
        }

        // § 4.4
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }
}           

從上述源碼内容來看,針對 Bean 的初始化邏輯由四部分組成。下面對這四部分内容進行逐一擊破!

§ 4.1 invokeAwareMethods()

invokeAwareMethods() 方法簡單得已經沒啥好說的了,直接貼代碼就完事了。

private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware beanNameAware) {
            beanNameAware.setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                beanClassLoaderAware.setBeanClassLoader(bcl);
            }
        }
        if (bean instanceof BeanFactoryAware beanFactoryAware) {
            beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}           

§ 4.2 applyBeanPostProcessorsBeforeInitialization()

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}           

從上述源碼内容來看,applyBeanPostProcessorsBeforeInitialization()方法主要就是周遊 BeanPostProcessor 并調用其 postProcessBeforeInitialization() 方法。 那麼究竟有哪些 BeanPostProcessor 實作類恰好實作了 postProcessBeforeInitialization() 方法呢? 比較重要的有三個,如下所示。

萬字長文!帶你探索 Bean 加載流程
下面以兩個小節分别對 ConfigurationPropertiesBindingPostProcessor 與 InitDestroyAnnotationBeanPostProcessor 進行剖析。

4.2.1 ConfigurationPropertiesBindingPostProcessor

大家也許知道,一個外部配置源與一個PropertySource是一一對應的,一個 PropertySource 執行個體中可以有多個配置項;但在 Spring 的地盤,還是要入鄉随俗,這些 PropertySource 均會被追加到Environment中 ! 而本小節的主角ConfigurationPropertiesBindingPostProcessor可以将外部配置源中的配置項綁定到由@ConfigurationProperties注解标注的 Bean 執行個體中,當然真正實作配置綁定的是 Spring Boot 自己提供的Binder API。

Binder API 中有四個核心類。Binder會進行真正地綁定操作,其提供多個綁定動作方法,如:bind()、bindObject() 和 bindOrCreate() 等方法;Bindable代表可綁定的目标,其支援泛型;BindResult代表綁定過後的結果,有點類似Optional;BindHandler用于在綁定過程中插入一些額外的邏輯,它提供了五個方法:onStart()、onSuccess()、onCreate()、onFailure() 和 onFinish()。

下面通過一段代碼來示範一下 Binder API 的魅力。

@Data
public class PersonProperties {
    private String firstName;
    private int age;
}

@SpringBootApplication
public class MumuApplication {
    public static void main(String[] args) {
        /*
         *        application.properties
         * =====================================
         * external-config.person.first-name=duxiaotou
         * external-config.person.age=18
         */
        ConfigurableApplicationContext wac = SpringApplication.run(MumuApplication.class, args);
        ConfigurableEnvironment environment = wac.getEnvironment();
        Binder binder = Binder.get(environment);
        Bindable<PersonProperties> bindableTarget = Bindable.of(PersonProperties.class);
        BindResult<PersonProperties> bindResult = binder.bind(ConfigurationPropertyName.of("external-config.person"), bindableTarget);
        PersonProperties personProperties = bindResult.get();
    }
}           
這裡想問大家一個問題:如果将 application.properties 配置檔案中的配置項修改為 external-config.person.FIRSTNAME、external-config.person.first_name 和 external-config.person.firstName ,那麼還能正常綁定嗎?答案是肯定的,這正是由 Spring Boot 提出的relaxed binding概念!

下面咱們再來看一看 ConfigurationPropertiesBindingPostProcessor 中 postProcessBeforeInitialization() 方法的内容。

public class ConfigurationPropertiesBindingPostProcessor 
        implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
    
    private ApplicationContext applicationContext;
    
    private ConfigurationPropertiesBinder binder;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
        return bean;
    }

    private void bind(ConfigurationPropertiesBean bean) {
        if (bean == null || hasBoundValueObject(bean.getName())) {
            return;
        }
        try {
            this.binder.bind(bean);
        } catch (Exception ex) {
            throw new ConfigurationPropertiesBindException(bean, ex);
        }
    }
}           

繼續跟進到ConfigurationPropertiesBinder中一探究竟。

public class ConfigurationPropertiesBinder {
    
    private final ApplicationContext applicationContext;

    private final PropertySources propertySources;

    private volatile Binder binder;

    public ConfigurationPropertiesBinder(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();
        this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext);
        this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext);
    }

    public BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
        Bindable<?> target = propertiesBean.asBindTarget();
        ConfigurationProperties annotation = propertiesBean.getAnnotation();
        BindHandler bindHandler = getBindHandler(target, annotation);
        return getBinder().bind(annotation.prefix(), target, bindHandler);
    }
}           

上述源碼内容明确地告訴我們:ConfigurationPropertiesBinder 正是通過 Binder API 來實作配置項綁定的。

熟悉 Environment 的讀者肯定知道,Environment 中包含多個不同名稱的 PropertySource,比如:command line args、servlet config init params、servlet context init params、system properties、system environment 和 annotation-{profile}.properties 等。但這些 PropertySource 是不具備relaxed binding能力的,那麼 Sping Boot 又是如何為這一衆 PropertySource 插上relaxed binding翅膀的呢?原來,Spring Boot 會提早插入一個名為configurationProperties的 PropertySourc,而且這個 PropertySource 位于 MutablePropertySources 最頂端。如下所示。

public final class ConfigurationPropertySources {
    
    private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
    
    public static void attach(Environment environment) {
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource<?> attached =
                new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources));
        sources.addFirst(attached);
    }
}           

上述 attach() 方法内容表明,這些不具備 relaxed binding 能力的 PropertySource 全部被丢進了SpringConfigurationPropertySources中,它的主要内容如下。

public class SpringConfigurationPropertySources implements Iterable<ConfigurationPropertySource> {

    private final Iterable<PropertySource<?>> sources;

    private final Map<PropertySource<?>, ConfigurationPropertySource> cache =
            new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.SOFT);

    public SpringConfigurationPropertySources(Iterable<PropertySource<?>> sources) {
        this.sources = sources;
    }

    @Override
    public Iterator<ConfigurationPropertySource> iterator() {
        return new SourcesIterator(this.sources.iterator(), this::adapt);
    }

    private ConfigurationPropertySource adapt(PropertySource<?> source) {
        ConfigurationPropertySource result = this.cache.get(source);
        if (result != null) {
            return result;
        }
        result = SpringConfigurationPropertySource.from(source);
        if (source instanceof OriginLookup) {
            result = result.withPrefix(((OriginLookup<?>) source).getPrefix());
        }
        this.cache.put(source, result);
        return result;
    }
}

public class SpringConfigurationPropertySource implements ConfigurationPropertySource {
    public static SpringConfigurationPropertySource from(PropertySource<?> source) {
        Assert.notNull(source, "Source must not be null");
        PropertyMapper[] mappers = getPropertyMappers(source);
        if (isFullEnumerable(source)) {
            return new SpringIterableConfigurationPropertySource((EnumerablePropertySource<?>) source, mappers);
        }
        return new SpringConfigurationPropertySource(source, mappers);
    }
}           

從 SpringConfigurationPropertySources 源碼内容來看,在對塞進來的 PropertySource 進行周遊的時候,會涉及适配操作。無非就是将一個個不具備 relaxed binding 能力的 PropertySource 适配成 SpringConfigurationPropertySource或者SpringIterableConfigurationPropertySource。SpringIterableConfigurationPropertySource 繼承自 SpringConfigurationPropertySource,這哥倆恰恰具備完整的 relaxed binding 能力!最後一個問題,誰會來調用 SpringConfigurationPropertySources 的 iterator() 方法呢?當然是 Binder 在進行配置項綁定的時候,會通過其findProperty方法觸發周遊動作。

public class Binder {
    private <T> ConfigurationProperty findProperty(ConfigurationPropertyName name,
                                                   Bindable<T> target,
                                                   Binder.Context context) {
        if (name.isEmpty() || target.hasBindRestriction(Bindable.BindRestriction.NO_DIRECT_PROPERTY)) {
            return null;
        }
        // context.getSources()
        //  ==> SpringConfigurationPropertySources#iterator()
        //   ==> SpringConfigurationPropertySources#adapt
        for (ConfigurationPropertySource source : context.getSources()) {
            ConfigurationProperty property = source.getConfigurationProperty(name);
            if (property != null) {
                return property;
            }
        }
        return null;
    }
}           

4.2.2 InitDestroyAnnotationBeanPostProcessor

InitDestroyAnnotationBeanPostProcessor主要用于調用目前 Bean 執行個體中由@PostConstruct以及@PreDestroy注解修飾的方法。 在其 postProcessBeforeInitialization() 方法内,首先通過 findLifecycleMetadata() 方法找到LifecycleMetadata執行個體,LifecycleMetadata 執行個體封裝了@PostConstruct以及@PreDestroy注解修飾的方法,同時這個 LifecycleMetadata 執行個體會被緩存到Map<Class<?>, LifecycleMetadata>類型的成員變量 lifecycleMetadataCache 中,然後調用該 LifecycleMetadata 執行個體的 invokeInitMethods() 方法。 詳細内容如下所示。

public class InitDestroyAnnotationBeanPostProcessor implements MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor {
    /**
     * jakarta.annotation.PostConstruct
     */
    private Class<? extends Annotation> initAnnotationType;
    /**
     * jakarta.annotation.PreDestroy
     */
    private Class<? extends Annotation> destroyAnnotationType;

    private final transient Map<Class<?>, LifecycleMetadata> lifecycleMetadataCache = new ConcurrentHashMap<>(256);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        try {
            metadata.invokeInitMethods(bean, beanName);
        } catch (InvocationTargetException ex) {
            throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
        }
        return bean;
    }

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        try {
            metadata.invokeDestroyMethods(bean, beanName);
        } catch (InvocationTargetException ex) {
            logger.warn("Destroy method on bean with name '" + beanName + "' threw an exception" + ": " + ex.getTargetException());
        } catch (Throwable ex) {
            logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
        }
    }

    private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
        if (this.lifecycleMetadataCache == null) {
            return buildLifecycleMetadata(clazz);
        }
        LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
        if (metadata == null) {
            synchronized (this.lifecycleMetadataCache) {
                metadata = this.lifecycleMetadataCache.get(clazz);
                if (metadata == null) {
                    metadata = buildLifecycleMetadata(clazz);
                    this.lifecycleMetadataCache.put(clazz, metadata);
                }
                return metadata;
            }
        }
        return metadata;
    }

    private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
        if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
            return this.emptyLifecycleMetadata;
        }

        List<LifecycleElement> initMethods = new ArrayList<>();
        List<LifecycleElement> destroyMethods = new ArrayList<>();
        Class<?> targetClass = clazz;

        do {
            final List<LifecycleElement> currInitMethods = new ArrayList<>();
            final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                    LifecycleElement element = new LifecycleElement(method);
                    currInitMethods.add(element);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
                    }
                }
                if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                    currDestroyMethods.add(new LifecycleElement(method));
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
                    }
                }
            });

            initMethods.addAll(0, currInitMethods);
            destroyMethods.addAll(currDestroyMethods);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);

        return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
                new LifecycleMetadata(clazz, initMethods, destroyMethods));
    }
}           

§ 4.3 invokeInitMethods()

invokeInitMethods()方法同樣簡單得不得了,它先調用InitializingBean的 afterPropertiesSet() 方法,然後調用 Custom Init Method (就是 @Bean 注解中 initMethod 屬性值所指定的方法)。

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null)) {
        ((InitializingBean) bean).afterPropertiesSet();
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
        String[] initMethodNames = mbd.getInitMethodNames();
        if (initMethodNames != null) {
            for (String initMethodName : initMethodNames) {
                if (StringUtils.hasLength(initMethodName) &&
                        !(isInitializingBean && "afterPropertiesSet".equals(initMethodName))) {
                    invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
                }
            }
        }
    }
}           

結合 § 4.2 和 § 4.3 這兩小節内容,我們可以得出一個重要結論:@PostConstruct 優先于 afterPropertiesSet() 方法執行,而 afterPropertiesSet() 又優先于 Custom Init Method 執行,如下圖所示。

§ 4.4 applyBeanPostProcessorsAfterInitialization()

顧名思義,applyBeanPostProcessorsAfterInitialization()方法用于周遊BeanPostProcessor,在周遊過程中調用其postProcessAfterInitialization()方法。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }
}           

BeanPostProcessor 最廣為人知的一個應用場景是用來橋接 Spring AOP 與 Spring IoC 容器,在 Spring AOP 中扮演這一橋接角色的就是AbstractAutoProxyCreator,大家如果對 Spring AOP 實作原理感興趣,可以從這個 BeanPostProcessor 實作類入手!

5 總結

盡管筆者寫了這麼多文字、貼了這麼多代碼、畫了這麼多圖,但單單靠閱讀一篇文章試圖搞懂 Bean 加載流程是不太現實的,還是需要大家親自去閱讀源碼,在閱讀源碼過程中,切記不要鑽牛角尖,否則隻會讓大家讀不下去,失去信心與勇氣,這是筆者的親身體驗。