BeanPostProcessor 是在何時介入 Bean 建立的?
來源:江南一點雨
今天來和各位小夥伴詳細分析一下 BeanPostProcessor。今天這篇是原理分析,基本用法松哥之前已經講過了,有影片也有文章,對影片感興趣的小夥伴戳這裡:Spring原始碼應該怎麼學?。
不同於前面和大家分享的 BeanFactoryPostProcessor,BeanPostProcessor 從名字上就能看出來,這是一個 Bean 的後置處理器,也就是說,BeanPostProcessor 其實主要是對已經建立出來的 Bean 做一些後置處理,而 BeanFactoryPostProcessor 主要是針對 BeanDefinition 做後置處理(此時 Bean 物件還沒建立出來)。
但是,BeanPostProcessor 家族裡邊也有例外,即 MergedBeanDefinitionPostProcessor,這是一個 BeanPostProcessor,但是卻可以處理 BeanDefinition。
這一切都是咋回事呢?我們今天就來分析分析。
1. BeanPostProcessor
首先我們先來看一下 BeanPostProcessor 介面的定義:
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
這裡就是兩個方法,理解這兩個方法有一個大的前提,就是此時 Spring 容器已經透過 Java 反射建立出來 Bean 物件了,只不過在初始化這個 Bean 物件的時候,又提供了一些配置介面:
postProcessBeforeInitialization:這個是在 Bean 初始化之前觸發,此時我們已經有一個 Bean 物件了,但是 Bean 中一些生命週期方法如 InitializingBean 介面的 afterPropertiesSet 方法、自定義的 init-method 方法等都尚未執行,在這些方法執行之前觸發 postProcessBeforeInitialization 方法。 postProcessAfterInitialization:類似於上面,在 afterPropertiesSet 和自定義的 init-method 之後觸發該方法。
BeanPostProcessor 的應用非常廣泛,在整個 Spring 體系中,也扮演了非常重要的角色,如 @Bean 註解的解析、AOP 動態代理的生成等等許多我們日常使用的功能,都是透過 BeanPostProcessor 來實現的。
2. MergedBeanDefinitionPostProcessor
MergedBeanDefinitionPostProcessor 算是整個 BeanPostProcessor 家族中比較另類的一個介面了,它雖然是 BeanPostProcessor,但是卻可以處理 BeanDefinition。MergedBeanDefinitionPostProcessor 介入的時機就是 Bean 建立成功之後,Bean 中各個屬性填充之前。
MergedBeanDefinitionPostProcessor 用於在 Bean 定義合併之後對合並後的 Bean 進行後置處理。它的作用是允許開發者在 Bean 定義合併完成後,對合並後的 Bean 進行自定義的修改或擴充套件操作。通常情況下,這個介面用於處理帶有註解的 Bean 定義,例如 @Autowired 或 @Value 等註解的處理。透過實現 MergedBeanDefinitionPostProcessor 介面,開發者可以在 Bean 定義合併後,對這些註解進行解析和處理,以實現自定義的邏輯。
來看下 MergedBeanDefinitionPostProcessor 介面:
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
default void resetBeanDefinition(String beanName) {
}
}
這裡就兩個方法,一個是處理合併後的 BeanDefinition,還有一個是重置 Bean 的。
3. 收集 BeanPostProcessor
接下來我們來看 BeanPostProcessor 的處理流程,首先第一步就是在容器啟動的時候,收集到使用者註冊在系統中的 BeanPostProcessor,無論是 Java 配置還是 XML 配置,在 refresh 方法中都會呼叫到 registerBeanPostProcessors,這個方法就是用來收集 BeanPostProcessor 的。
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
這裡的程式碼我就不逐行解釋了,我來說一下整體的處理思路。
這裡用來儲存 BeanPostProcessor 的集合一共有四個,分別是:
priorityOrderedPostProcessors:由於 BeanPostProcessor 可能存在多個,所以我們需要給這多個 BeanPostProcessor 進行排序,排序的方式有兩種,一種就是在定義 BeanPostProcessor 的時候,讓其實現 PriorityOrdered 介面,那麼這裡就是把所有實現了 PriorityOrdered 介面的 BeanPostProcessor 收集到一起。 orderedPostProcessors:類似於上面的,這裡是收集所有實現了 Ordered 介面的 BeanPostProcessor。 nonOrderedPostProcessors:這個裡邊儲存了所有不需要排序的 BeanPostProcessor。 internalPostProcessors:這個裡邊儲存了 MergedBeanDefinitionPostProcessor 型別的 BeanPostProcessor,前三種互相之間不會重複,而 internalPostProcessors 可能會和前三種有重複。
將收集並且排序好的 BeanPostProcessor,呼叫 registerBeanPostProcessors 方法進行註冊:
private static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<? extends BeanPostProcessor> postProcessors) {
if (beanFactory instanceof AbstractBeanFactory abstractBeanFactory) {
// Bulk addition is more efficient against our CopyOnWriteArrayList there
abstractBeanFactory.addBeanPostProcessors(postProcessors);
}
else {
for (BeanPostProcessor postProcessor : postProcessors) {
beanFactory.addBeanPostProcessor(postProcessor);
}
}
}
這裡最終就是把收集到的 BeanPostProcessor 新增到容器的 beanPostProcessors 變數中。
現在,容器中已經有了 BeanPostProcessor 了,接下來看什麼時候執行。
4. 觸發 BeanPostProcessor
BeanPostProcessor 的執行分為兩種情況,一種是執行 MergedBeanDefinitionPostProcessor 型別的 BeanPostProcessor,還有一種是執行普通的 BeanPostProcessor,我們分別來看。
在建立 Bean 的關鍵方法 AbstractAutowireCapableBeanFactory#doCreateBean 中,有這樣幾個關鍵步驟:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 初始化 Bean 例項
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
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();
}
}
//省略
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//省略
return exposedObject;
}
大家看到,在初始化 Bean 例項之後,有一個 applyMergedBeanDefinitionPostProcessors 方法,這個方法就是用來觸發 MergedBeanDefinitionPostProcessor 執行的。
populateBean 方法是給 Bean 的各個屬性填充值的,填充完成之後,呼叫 initializeBean 方法進行剩餘的初始化工作,在 initializeBean 方法中,呼叫了其餘的 BeanPostProcessor。
4.1 觸發 MergedBeanDefinitionPostProcessor
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
4.2 觸發其他 BeanPostProcessor
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
大家看這個順序,先是調 applyBeanPostProcessorsBeforeInitialization,這個裡邊最終就觸發到了 BeanPostProcessor#postProcessBeforeInitialization 方法;然後呼叫 invokeInitMethods,afterPropertiesSet 和自定義的 init-method 都在這裡被觸發;最後呼叫 applyBeanPostProcessorsAfterInitialization,這個裡邊最終就觸發到 BeanPostProcessor#postProcessAfterInitialization 方法。
好啦,這就是和小夥伴們梳理的 BeanPostProcessor 原理了,感興趣的小夥伴可以自己 DEBUG 跑一遍哦~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2990425/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring bean到底是如何建立的?(上)SpringBean
- Spring IoC bean 的建立(上)SpringBean
- spring4.1.8擴充套件實戰之七:控制bean(BeanPostProcessor介面)Spring套件Bean
- Spring建立Bean的過程DebugSpringBean
- 何時使用Component還是PureComponent?
- session是什麼時候建立的Session
- Mysql可重複讀(1) —— 快照何時建立MySql
- Spring 原始碼(17)Spring Bean的建立過程(8)Bean的初始化Spring原始碼Bean
- 【Spring註解驅動開發】BeanPostProcessor在Spring底層是如何使用的?看完這篇我懂了!!SpringBean
- 在量子計算時代,企業資料的加密該何去何從?加密
- 【spring 原始碼】IOC 之bean例項的建立Spring原始碼Bean
- BeanPostProcessor 介面和@PostConstruct 在使用姿勢上差異BeanStruct
- spring原始碼閱讀筆記08:bean載入之建立beanSpring原始碼筆記Bean
- ① EJB無狀態的bean(建立EJB的基礎教程)Bean
- Spring中用註解建立bean例項SpringBean
- Spring 原始碼(12)Spring Bean 的建立過程(3)Spring原始碼Bean
- Spring 原始碼(11)Spring Bean 的建立過程(2)Spring原始碼Bean
- Spring 原始碼(13)Spring Bean 的建立過程(4)Spring原始碼Bean
- Spring 原始碼(10)Spring Bean 的建立過程(1)Spring原始碼Bean
- Spring 原始碼(14)Spring Bean 的建立過程(5)Spring原始碼Bean
- 死磕Spring之IoC篇 - Bean 的建立過程SpringBean
- 何時用 struct?何時用 class?Struct
- Rust中何時應使用 String 還是 &str?Rust
- 談談Spring中的BeanPostProcessor介面SpringBean
- 在Java中建立物件的不同方法是什麼?Java物件
- [譯] 在JavaScript中何時使用var、let及constJavaScript
- 專案介入EF Core
- Spring原始碼分析之Bean的建立過程詳解Spring原始碼Bean
- 【雜談】tocmat是何時寫回響應資料包的
- 你是何時注意到 父母要住養老院的?
- JS中if語句何時認為條件的求值是falseJSFalse
- spring原始碼深度解析— IOC 之 bean 建立Spring原始碼Bean
- 恆訊科技分析:何時是切換到VPS主機的合適時機?
- 談談Spring中的BeanPostProcessor介面(轉)SpringBean
- Spring 原始碼(9)Spring Bean的建立過程的前期準備Spring原始碼Bean
- 為何 Canvas 內元素動畫總是在顫抖?Canvas動畫
- Sora 時代的 AI 影片生成何去何從?SoraAI
- spring中FactoryBean是什麼beanSpringBean