本文基于 Spring Boot 3.0.0 (Spring 6.0.2),Bean 指的是 Singleton Bean。
宏觀地說,Bean 加載流程大緻有三個階段,分别是執行個體化 createBeanInstance() 、屬性填充 populateBean() 和 初始化 initializeBean(),當 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 的邏輯,從上面代碼中,我們可以很輕松地總結出以下規律:
- 若 @Configuration 注解中 proxyBeanMethods 屬性值為 true,則執行 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
- 若 @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 類型的多級緩存,的确有些讓人崩潰 (後面會解答),但總體邏輯還是清晰的。
- 首先從一級緩存 singletonObjects 中擷取 Bean 執行個體,如果擷取到了,則直接傳回。
- 如果從一級緩存中未擷取到且目前 Bean 正處于加載流程中,然後從二級緩存 earlySingletonObjects 裡面擷取,如果擷取到了,則直接傳回。
- 如果從二級緩存中還是沒有擷取到的話,那麼從三級緩存 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() 方法主要邏輯有:
- 如果 name 以 & 為字首且 Bean 執行個體是 BeanFactory 類型的,那目前 Bean 執行個體就是最終想要的。
- 如果 name 不以 & 為字首且 Bean 執行個體不是 BeanFactory 類型的,那目前 Bean 執行個體就是最終想要的。
- 如果 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 執行個體的核心外圍邏輯。
- 首先,向 singletonsCurrentlyInCreation 成員變量中插入 beanName,辨別該 Bean 正處于加載流程中。
- 其次,回調ObjectFactory的getObject()方法,getObject() 方法中的全部邏輯就是調用 createBean() 方法,createBean() 方法涵蓋了完整的 Bean 加載流程。
- 再次,此時 Bean 執行個體不僅建立完畢,而且還完成了屬性填充 populateBean() 和 初始化 initializeBean() 流程,真正具備使用的條件了,那麼自然要從 singletonsCurrentlyInCreation 中删除該 beanName,辨別該 Bean 加載流程走完了。
- 最後,将 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() 方法前有兩個知識點需要大家去關注。
- 如果目前 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));
}
- 大家應該清楚 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 時序圖,如下所示。
在上述 doCreateBean() 方法的源碼中,我們終于看到了 createBeanInstance()、populateBean() 和 initializeBean() 的身影。剛剛說了,populateBean() 和 initializeBean() 這倆方法在後續章節分析,而筆者也不打算分析 createBeanInstance() 方法的内容,這個内容真是晦澀難懂啊,記住 “Bean 執行個體主要是通過反射機制來建構” 這一點就行了。
在 createBeanInstance() 方法執行完之後,緊接着執行applyMergedBeanDefinitionPostProcessors()方法,主要是周遊MergedBeanDefinitionPostProcessor并調用其postProcessMergedBeanDefinition()方法。MergedBeanDefinitionPostProcessor 的應用時機介于 執行個體化 createBeanInstance() 與 屬性填充 populateBean() 之間,為後續 屬性填充 populateBean() 和 初始化 initializeBean() 階段提前緩存LifecycleMetadata和InjectionMetadata。MergedBeanDefinitionPostProcessor 接口有三個重要的實作類,如下所示。
- 在 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 是完全有能力破局的。循環依賴可以分為下圖中的這兩種類型,其他情形的循環依賴都是它們的變體而已。
為了解決循環依賴問題,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 是一個代理。
- 嘗試從多級緩存中加載 Bean A,此時肯定是加載不到的,必須要完整走一遍 Bean 加載流程了。
- createBeanInstance() 方法為 Bean A 生成原生的 Bean 執行個體。
- 将 Bean A 的執行個體提前暴露到三級緩存 singletonObjects 中,此時由于需要為 Bean A 生成代理,那麼最終躺在三級緩存 singletonObjects 中的是其代理。
- populateBean() 方法為 Bean A 進行屬性填充,這時發現需要為 Bean A 注入 Bean B。
- 嘗試從多級緩存中加載 Bean B,此時肯定是加載不到的,必須要完整走一遍 Bean 加載流程了。
- createBeanInstance() 方法為 Bean B 生成 Bean 執行個體。
- 将 Bean B 的執行個體提前暴露到三級緩存 singletonObjects 中。
- populateBean() 方法為 Bean B 進行屬性填充,這時發現需要為 Bean B 注入 Bean A,自然會觸發對 Bean A 的加載流程。
- 這個時候已經可以從多級緩存中加載到 Bean A 了,因為它此刻正躺在三級緩存 singletonObjects 中呢。
- 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 屬性填充
關于屬性填充或者屬性注入的概念,大家或多或少都了解一些,下面就一起來看看它的全部邏輯吧。
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 中呢?
- 首先,在一級緩存 singletonObjects 與 beanDefinitionMap 二者中至少有一個能比對到該 beanName。
- 其次,該成員變量必須要有相應的 setter 方法;由 @Autowired 或 @Resource 注解标注的成員變量一般是過不了這一關的,因為咱們平時很少為它們編寫 setter 方法。
- 再次,該成員變量名稱在目前 RootBeanDefinition 執行個體所持有的成員變量 MutablePropertyValues 中必須是不存在。
- 最後,該成員變量的類型不能是一種Simple Property,具體參考下圖。
§ 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 為其提供了兩個極其重要的實作類,如下圖所示。
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 執行個體實作依賴注入的,如下圖所示。
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() 方法呢? 比較重要的有三個,如下所示。
下面以兩個小節分别對 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 加載流程是不太現實的,還是需要大家親自去閱讀源碼,在閱讀源碼過程中,切記不要鑽牛角尖,否則隻會讓大家讀不下去,失去信心與勇氣,這是筆者的親身體驗。