Spring Framework 源碼閱讀(五):BeanFactoryPostProcessor
在上一篇部落格中介紹了
BeanPostProcessor
和
Bean
的生命周期,
BeanPostProcessor
是允許自定義修改新
bean
執行個體的工廠鈎子,在新
bean
執行個體初始化前後調用
BeanPostProcessor
中的方法,而通過
FactoryBean
建立的新
bean
執行個體和
Spring
通過反射建立的新
bean
執行個體在應用
BeanPostProcessor
方面有所不同,前者隻會調用
BeanPostProcessor
中的
postProcessAfterInitialization
方法,而後者會調用
BeanPostProcessor
中的所有方法,想詳細了解這些内容可以閱讀這篇部落格:
- Spring Framework 源碼閱讀(四):BeanPostProcessor和Bean的生命周期
那
BeanFactoryPostProcessor
有啥用?看命名跟
BeanPostProcessor
差不多,其實功能也差不多,都是自定義修改執行個體,隻是修改的主體不同,
BeanPostProcessor
修改的主體是
bean
,而
BeanFactoryPostProcessor
修改的主體是
bean
定義,不了解
bean
定義,可以閱讀這篇部落格:
- Spring Framework 源碼閱讀(二):BeanDefinition的作用
注意,
BeanFactoryPostProcessor
和
BeanPostProcessor
都是自定義修改執行個體(執行個體主體不同),在兩者應用時,執行個體肯定是已經執行個體化了(通過反射調用或者
FactoryBean
直接調用這兩種方式,調用了執行個體類的構造函數)。
BeanFactoryPostProcessor
接口源碼如下(
@FunctionalInterface
表示該接口是函數式接口,可以使用
lambda
表達式直接指派):
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
- 工廠鈎子:允許自定義修改應用程式上下文(
)的 application context
定義,調整上下文底層 bean
工廠的 bean
屬性值。對于自定義配置檔案很有用,這些檔案覆寫了應用程式上下文中配置的 bean
屬性。 有關滿足此類配置需求的開箱即用解決方案,請參閱bean
及其具體實作。PropertyResourceConfigurer
可以與BeanFactoryPostProcessor
定義互動并修改bean
定義,但絕不能與bean
執行個體互動(這樣做可能會導緻 bean
過早執行個體化、違反容器意願并導緻意外的副作用)。 如果需要與bean
執行個體互動,請考慮實作bean
。BeanPostProcessor
- 注冊:
在其ApplicationContext
定義中自動檢測bean
,并在建立任何其他BeanFactoryPostProcessor bean
之前應用它們。 bean
也可以通過BeanFactoryPostProcessor
以程式設計方式注冊。ConfigurableApplicationContext
- 順序:在
中自動檢測的ApplicationContext
将根據BeanFactoryPostProcessor bean
和PriorityOrdered
語義進行排序。 相比之下,使用Ordered
以程式設計方式注冊的ConfigurableApplicationContext
将按注冊順序應用; 對于以程式設計方式注冊的BeanFactoryPostProcessor bean
,通過實作PostProcessor
或PriorityOrdered
接口表達的任何排序語義都将被忽略。 此外,Ordered
注解不會被@Order
考慮在内。BeanFactoryPostProcessor bean
部落客早期的部落格,可能排版不好看以及語言不嚴謹,也推薦閱讀一下,可以對函數式接口(
@FunctionalInterface
)有一個直覺的認識。
- JDK8 新特性Function接口
建立module
先在
Spring Framework
源碼中增加一個
application module
,這在之前的博文中已經介紹過了,這裡就不再贅述:
- 編譯 Spring Framework 5.2.17源碼 & 在源碼中使用 ApplicationContext 擷取定義的Bean
TaskBeanDefinitionProcess
類(實作
BeanFactoryPostProcessor
接口):
package com.kaven.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* @Author: ITKaven
* @Date: 2021/11/03 10:34
* @Leetcode: https://leetcode-cn.com/u/kavenit
* @Notes:
*/
@Component
public class TaskBeanDefinitionProcess implements BeanFactoryPostProcessor {
// 将自定義的作用域字元串myScope轉換成singleton
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("myTask");
if(Objects.equals(bd.getScope(), "myScope")) {
bd.setScope("singleton");
}
}
}
啟動類
Application
:
package com.kaven;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.*;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: ITKaven
* @Date: 2021/09/25 13:54
* @Leetcode: https://leetcode-cn.com/u/kavenit
* @Notes:
*/
@ComponentScan({"com.kaven"})
public class Application {
public static void main(String[] args) {
// 建立應用上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 在應用上下文中添加BeanFactoryPostProcessor
// 将myTask bean的BeanDefinition的懶加載設定為false
applicationContext.addBeanFactoryPostProcessor((beanFactory) -> {
beanFactory.getBeanDefinition("myTask").setLazyInit(false);
});
// 注冊元件類
applicationContext.register(Application.class);
// refresh
applicationContext.refresh();
// 擷取myTask bean
Task task = (Task) applicationContext.getBean("myTask");
System.out.println("taskCount: " + task.addTask());
// 擷取myTask bean的BeanDefinition
BeanDefinition taskBeanDefinition = applicationContext.getBeanDefinition("myTask");
System.out.println(taskBeanDefinition.getScope());
System.out.println(taskBeanDefinition.isLazyInit());
// 擷取應用上下文中的BeanDefinition數量和名稱
System.out.println("BeanDefinitionCount: " + applicationContext.getBeanDefinitionCount());
Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
}
// myTask bean
@Bean(value = "myTask")
@Scope("myScope")
@Lazy
public Task getTask() {
return new Task();
}
public static class Task{
private final AtomicInteger taskCount;
public Task() {
taskCount = new AtomicInteger(0);
}
public int addTask() {
return taskCount.incrementAndGet();
}
}
}
輸出結果:
taskCount: 1
singleton
false
BeanDefinitionCount: 7
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
application
taskBeanDefinitionProcess
myTask
很顯然符合預期,
myTask bean
的作用域從自定義作用域字元串
myScope
轉換成了
singleton
,懶加載也設定成了
false
,這些都是因為
TaskBeanDefinitionProcess
類的
postProcessBeanFactory
方法以及下方的
lambda
表達式起作用了。
(beanFactory) -> {
beanFactory.getBeanDefinition("myTask").setLazyInit(false);
}
源碼分析
開始
Debug
。
執行這行代碼:
applicationContext.refresh();
調用了
AbstractApplicationContext
抽象類的
refresh
方法(删除了部分代碼):
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準備此上下文以進行重新整理
prepareRefresh();
// 告訴子類重新整理内部bean工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準備在此上下文中使用的bean工廠
prepareBeanFactory(beanFactory);
try {
// 允許在上下文子類中對bean工廠進行postProcess
postProcessBeanFactory(beanFactory);
// 調用在上下文中注冊的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 為此上下文初始化事件多點傳播器
initApplicationEventMulticaster();
// 初始化特定上下文子類中的其他特殊bean
onRefresh();
// 檢查監聽器bean并注冊它們
registerListeners();
// 執行個體化所有剩餘的(非延遲加載)單例
finishBeanFactoryInitialization(beanFactory);
// 釋出相應的事件
finishRefresh();
}
}
}
之後會調用
PostProcessorRegistrationDelegate
類的
invokeBeanFactoryPostProcessors
方法(删除了部分代碼,看注釋即可):
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 如果有的話,首先調用BeanDefinitionRegistryPostProcessor
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
// 調用BeanDefinitionRegistryPostProcessor
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 調用到目前為止處理的所有處理器的postProcessBeanFactory回調
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// 調用在上下文執行個體中注冊的BeanFactoryPostProcessor
// 但beanFactory不能instanceof BeanDefinitionRegistry
// 如果beanFactory instanceof BeanDefinitionRegistry
// 執行上面的invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory)
// 也會調用在上下文執行個體中注冊的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 擷取BeanFactoryPostProcessor bean的名稱
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// 調用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除緩存的合并bean定義
// 因為BeanFactoryPostProcessor可能已經修改了原始中繼資料,例如替換值中的占位符
beanFactory.clearMetadataCache();
}
BeanDefinitionRegistryPostProcessor
接口源碼:
// 對标準BeanFactoryPostProcessor SPI的擴充
// 允許在正常BeanFactoryPostProcessor檢測開始之前注冊bean定義
// 特别是, BeanDefinitionRegistryPostProcessor可以注冊BeanFactoryPostProcessor bean定義。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
// 在标準初始化之後修改應用程式上下文的内部bean定義系統資料庫
// 所有正常bean定義都将被加載,但尚未執行個體化任何bean
// 這允許在下一個後處理階段開始之前添加更多的bean定義
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
是以,
BeanFactoryPostProcessor
應用時,
bean
定義已經注冊好了,和前面描述的一緻。
但我們并沒有提供
BeanDefinitionRegistryPostProcessor
接口的實作,為什麼還能注冊
bean
定義?還記得代碼的輸出結果中有幾個
Spring
内置的
bean
定義:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
對應的
bean
類是
ConfigurationClassPostProcessor
類。
這在
BeanDefinition
的作用這篇部落格中有介紹。
/**
* 内部管理Configuration注解的處理器的bean名稱
*/
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
ConfigurationClassPostProcessor
類(實作了
BeanDefinitionRegistryPostProcessor
接口):
// 這個PostProcessor是優先級排序的,因為在任何其他BeanFactoryPostProcessor執行之前
// 在@Configuration類中聲明的任何@Bean方法都必須注冊其相應的bean定義,這一點很重要
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
...
}
當然我們這裡沒有使用
@Configuration
注解,但很顯然
@ComponentScan({"com.kaven"})
的效果也是類似的。
再回到
PostProcessorRegistrationDelegate
類的
invokeBeanFactoryPostProcessors
方法,通過
Debug
,可以知道在調用
invokeBeanFactoryPostProcessors
方法前,如下圖所示的
5
個
bean
定義就已經注冊了,這在
BeanDefinition
的作用這篇部落格中有介紹,這裡不再贅述。是以,還剩下
myTask
和
taskBeanDefinitionProcess
這兩個
bean
的
bean
定義沒有注冊。
調用了
ConfigurationClassPostProcessor
類的
postProcessBeanDefinitionRegistry
方法後,
myTask
和
taskBeanDefinitionProcess
這兩個
bean
的
bean
定義也會被注冊。
而
myTask bean
的
bean
定義還沒有被修改,因為自定義的兩個
BeanFactoryPostProcessor
還沒有應用。
在這裡會應用使用
lambda
表達式定義的
BeanFactoryPostProcessor
(
beanFactory instanceof BeanDefinitionRegistry
為
true
,不然下面
else
部分那一行代碼也會執行該
BeanFactoryPostProcessor
)。
懶加載設定成了
false
,而作用域還是自定義的作用域字元串
myScope
。
在這裡會應用我們實作的
BeanFactoryPostProcessor
(
TaskBeanDefinitionProcess
類)。
到這裡作用域也修改成功了。
BeanFactoryPostProcessor
能被應用,說明這些
BeanFactoryPostProcessor bean
已經建立好了(能修改其他
bean
的
bean
定義,當然需要提取建立好,以便應用,當然使用
lambda
表達式定義的
BeanFactoryPostProcessor
不需要被建立,類似使用匿名内部類,本身就是一個執行個體,隻是原理有些不同):
invokeBeanFactoryPostProcessors
方法開始執行時,還沒有已經建立的對象。
執行完下面這部分代碼,
ConfigurationClassPostProcessor
類對應的
bean
就被建立好了(通過
beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)
建立),該
bean
也會注冊之前說過的那兩個
bean
定義。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
執行完下面這部分代碼,
BeanFactoryPostProcessor bean
(
TaskBeanDefinitionProcess
執行個體)也建立好了(通過
beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)
建立)。
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}