Springboot擴展點之BeanFactoryPostProcessor
前言
圖片
功能特性
- BeanFactoryPostProcessor的執行是Spring Bean生命周期非常重要的一部分;
- BeanFactory級別的后置處理器,在Spring生命周期內,org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory只會執行一次;
- 允許在容器讀取到Bean的BeanDefinition數據之后,bean未實例化前,讀取BeanDefiniion數據,并且可以根據需要進行修改;
實現方式
1、定義一個Dog類,name屬性默認為“旺財”,顏色默認為“黑色”;
@Data
@Component
public class Dog {
private String name="旺財";
private String color="黑色";
}
2、定義一個實現類(MyBeanFactoryPostProcessor),實現BeanFactoryPostProcessor接口,重寫postProcessBeanFactory()方法,并Dog類的屬性name修改為“狗蛋”;并用@Component注解標記BeanFactoryPostProcessor接口的實現類(MyBeanFactoryPostProcessor)
@Component
@Slf4j
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
log.info("com.fanfu.config.MyBeanFactoryPostProcessor.postProcessBeanFactory被執行");
ScannedGenericBeanDefinition dog = ((ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("dog")) ;
MutablePropertyValues propertyValues = dog.getPropertyValues();
propertyValues.addPropertyValue("name", "狗蛋兒");
log.info("修改Dog的BeanDefinition對象中的name屬性為狗蛋兒");
}
}
3、編寫單元測試驗證結果
@SpringBootTest
@Slf4j
public class FanfuApplicationTests {
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
Dog dog = ((Dog) context.getBean("dog"));
log.info(dog.getName());
Assert.isTrue(dog.getName().equals("狗蛋兒"), "屬性修改失敗");
}
}
4、驗證結果表明,自定義的BeanFactoryPostProcessor接口的實現類(MyBeanFactoryPostProcessor),可以在容器讀取到Bean的BeanDefinition數據之后,bean未實例化前,讀取BeanDefiniion數據,并且根據需要進行修改,那么自定義的BeanFactoryPostProcessor接口的實現類(MyBeanFactoryPostProcessor)的工作原理是什么呢?BeanFactoryPostProcessor接口的實現類是什么時候實例化的?MyBeanFactoryPostProcessor#postProcessBeanFactory方法是如何被調用的?接著往下看。
工作原理
BeanFactoryPostProcessor接口的實現類是什么時候實例化的?
1、BeanFactoryPostProcessor接口的實現類(MyBeanFactoryPostProcessor)被@Component標記,在窗口啟動的時候會被封裝成BeanDefinition對象注冊到容器中;
圖片
2、PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法被執行時,會按照類型從Spring容器中找到所有BeanFactoryPostProcessor類型的實現類的名稱;
圖片
3、在上一步中得到所有BeanFactoryPostProcessor類型的實現類的名稱的名稱后,再循環一次,來對BeanFactoryPostProcessor的實現類進行實例化 (beanFacotry.getBean()去獲取MyBeanFactoryPostProcessor的實例,如果獲取不到,就創建一個);
圖片
MyBeanFactoryPostProcessor#postProcessBeanFactory方法是如何被調用的?
1、在單元測試中構建了一個AnnotationConfigApplicationContext對象,AnnotationConfigApplicationContext的構造方法如下:
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
2、從上面的AnnotationConfigApplicationContext的構造方法中,可以看到又調用了refresh(),這里實際最終被調用的是AbstractApplicationContext#refresh(),這個方法也是Spring容器啟動的關鍵方法,在分析Spring相關的源碼時會經常碰到。
3、AbstractApplicationContext#refresh()中,調用AbstractApplicationContext#invokeBeanFactoryPostProcessors方法才正式開始了BeanFactoryPostProcessor接口的所有實現類的postProcessBeanFactory()方法調用;
4、跟著AbstractApplicationContext#invokeBeanFactoryPostProcessors方法進去,會發現這里只是一個入口,實際承擔調用執行任務的是PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法;
5、跟著PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法進去后,會發現里面真的是別有洞天,很容易迷路(牢牢帶著自己的問題分析源碼找答案,不要被除自己問題以外的東西迷了眼,一定會柳暗花明),另外org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor的實現類的調用也在這個方法,所以這個方法的含金量很高,那就單獨拎出來仔細分析一下,建議debug一步一步看,多看幾遍就能明白,其實也很簡單,唯一的難點就是這個方法有點長,需要多點耐心和時間。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
//之所以說這個方法的含金量很高,
//是因為在這個方法里是先執行BeanDefinitionRegistryPostProcessor實現類的postProcessBeanDefinitionRegistry方法;//然后才接著執行BeanFactoryPostProcessor接口的實現類的postProcessBeanFactory方法
//這兩個接口很表面上看很類似,實際在執行的時機和功能上是有明顯區別的
Set<String> processedBeans = new HashSet<>();
//AnnotationConfigApplicationContext繼承于GenericApplicationContext,
//而GenericApplicationContext又實現了BeanDefinitionRegistry接口
//所以這里會進入if語句中
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
//這里提前執行的BeanFactoryPostProcessor,是在準備上下文環境時,發布了ApplicationPreparedEvent事件;//觸發監聽器,通過AbstractApplicationContext#addBeanFactoryPostProcessor注冊進來的;//這里并不是這次要重點分析的內容,可以先跳過這;for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 從32行到72行,是在執行BeanDefinitionRegistryPostProcessor實現類的postProcessBeanDefinitionRegistry方法;//執行的過程也是有點小區折的,分三步,第一,執行實現了PriorityOrdered接口的實現類.
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();
// 第二,執行實現了Ordered接口的實現類;postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
//第三,執行剩下其他的BeanDefinitionRegistryPostProcessor實現類;boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor,
//所以這部分實現類的postProcessBeanFactory()會提前執行
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
//第26行,非BeanDefinitionRegistryPostProcessor類型的BeanFactoryPostProcessor實現類會在這執行
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else {
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
//BeanDefinitionRegistryPostProcessor接口的實現類上面已執行執行完了
//下面開始準備執行BeanFactoryPostProcessor接口的實現類
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// 正式執行前,把BeanFactoryPostProcessor接口的實現類分成了三類,
//分別是實現了PriorityOrdered接口,實現了Ordered接口,其他;
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 分好類,第一,先執行實現了PriorityOrdered接口的實現類;sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 第二,執行實現了Ordered接口的實現類;List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
//第三,執行未實現上面兩個接口的實現類,自定義的MyBeanFactoryPostProcessor就是在這里被執行的
//其實,也很簡單的,和BeanDefinitionRegistryPostProcessor接口的實現類的執行過程類似;List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
beanFactory.clearMetadataCache();
}
調用時序圖
這里我畫了一個時序圖,可以更直觀的看到整個調用過程,也可以照著這個圖,一步一步debug來了解整個過程;
圖片
postProcessBeanFactory()與postProcessBeanDefinitionRegistry()
通過分析PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法,postProcessBeanFactory()與postProcessBeanDefinitionRegistry()的區別已經很明顯了,這里再總結一下(總結的有不準的地方,還請小伙伴在評論區告訴我,一塊進步):
- BeanDefinitionRegistryPostProcessor接口的實現類的postProcessBeanDefinitionRegistry方法要優先于BeanFactoryPostProcessor接口的實現類的postProcessBeanFactory方法執行;
- postProcessBeanDefinitionRegistry方法形參是BeanDefinitionRegistry,postProcessBeanFactory方法的形參是ConfigurableListableBeanFactory,在功能上會有一些區別;需要注意的是DefaultListableBeanFactory實現了BeanDefinitionRegistry和ConfigurableListableBeanFactory接口;
- BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor,關于BeanDefinitionRegistryPostProcessor可以移步這里:Springboot擴展點之BeanDefinitionRegistryPostProcessor;
應用場景
對敏感信息的解密處理,比如數據庫連接信息加密和解密:在實際的業務開發中,在配置文件中明文配置mysq,redis的密碼實際上是不安全的,需要配置加密后的密碼信息;但是把加密后的密碼信息注入的數據源中,去連接mysql數據庫肯定會連接異常,因為mysql并不知道你的加密方式和加密方法。這就會產生一個需求:需要在配置文件中配置的數據庫信息是加密的,但是在把密碼信息注入數據源前在程序里解密處理。
BeanFactoryPostProcessor正好可以解決這個問題,在真正使用到數據源去連接數據庫前,讀取到加密信息,進行解密處理,再用解密后的信息替換掉Spring容器中加密信息。