1. BeanFactoryPostProcessor 概覽
1.1 解讀 BeanFactoryPostProcessor
??BeanFactoryPostProcessor
位于org.springframework.beans.factory.config
包中。它與BeanPostProcessor
有相似的核心邏輯,但它們之間的主要區(qū)別在于它們所操作的對象。BeanFactoryPostProcessor
的主要目的是對Bean
的配置元數(shù)據(jù)進行操作,這意味著它可以影響Bean
的初始配置數(shù)據(jù)。
??在Spring IoC
容器實例化beans
之前,特別是除了BeanFactoryPostProcessor
之外的其他beans
,BeanFactoryPostProcessor
有權(quán)利修改這些beans
的配置。在Spring
中,所有的beans
在被完全實例化之前都是以BeanDefinition
的形式存在的。BeanFactoryPostProcessor
為我們提供了一個機會,使我們能夠在bean
完全實例化之前調(diào)整和修改這些BeanDefinition
。對BeanDefinition
的任何修改都會影響后續(xù)的bean
實例化和初始化過程。
1.2. 如何使用 BeanFactoryPostProcessor
來看看BeanFactoryPostProcessor
能如何影響BeanDefinition
。
假設(shè)我們需要為一系列的Tint
對象賦值名字,這個名字就是bean
的名字,而且要在bean
實例化之前完成。
- 定義bean
我們定義一個簡單的Tint
抽象類以及其兩個子類Blue
和Yellow
:
package com.example.demo.bean;
public abstract class Tint {
protected String label;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Blue extends Tint {
@Override
public String toString() {
return "Blue{" + "label='" + label + '\'' + "}";
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Yellow extends Tint {
@Override
public String toString() {
return "Yellow{" + "label='" + label + '\'' + "}";
}
}
- 創(chuàng)建后置處理器
思路是在后置處理器中,我們可以獲取到BeanFactory
,然后操作其中的BeanDefinition
。
package com.example.demo.processor;
import com.example.demo.bean.Tint;
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 org.springframework.util.ClassUtils;
/**
* BeanFactory后置處理器,用于設(shè)置Tint子類bean的label屬性。
* label屬性的值會設(shè)置為"postProcessBeanFactory_" + beanName。
*/
@Component
public class TintLabelSetterFactoryPostProcessor implements BeanFactoryPostProcessor {
/**
* 在所有BeanDefinition加載完成之后,但bean實例化之前,設(shè)置label屬性。
*
* @param beanFactory 可配置的bean工廠,可以操作BeanDefinition。
* @throws BeansException 處理過程中的異常。
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 遍歷所有bean的名字
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 檢查bean的類名是否非空,且其父類是Tint
if (beanDefinition.getBeanClassName() != null &&
ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), this.getClass().getClassLoader())
.getSuperclass().equals(Tint.class)) {
// 添加或更新(如果屬性已存在)label屬性的值
beanDefinition.getPropertyValues().add("label", "postProcessBeanFactory_" + beanName);
}
}
}
}
- 運行測試
啟動Spring
容器,查看結(jié)果:
package com.example.demo;
import com.example.demo.bean.Blue;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.example.demo");
Blue blue = ctx.getBean(Blue.class);
System.out.println(blue);
}
}
運行之后,控制臺打印Blue
對象的label
屬性,顯示后置處理器成功修改了bean
的屬性。
- 替代方法
我們也可以使用BeanPostProcessor
達到BeanFactoryPostProcessor
相似的效果:
package com.example.demo.processor;
import com.example.demo.bean.Tint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* TintLabelSetterPostProcessor類是一個BeanPostProcessor的實現(xiàn),
* 它為類型為Tint的bean設(shè)置'label'屬性。該屬性的值將被設(shè)置為"postProcessAfterInitialization_"加上bean的名稱。
* 這里是一個postProcessAfterInitialization方法,它會在bean初始化后,但在返回給調(diào)用者之前執(zhí)行。
*/
@Component
public class TintLabelSetterPostProcessor implements BeanPostProcessor {
/**
* 對bean進行后初始化處理。如果bean是Tint類型,它的'label'屬性將被設(shè)置。
*
* @param bean 將要處理的bean對象。
* @param beanName bean的名稱。
* @return 可能已經(jīng)修改過的bean。
* @throws BeansException 如果在處理過程中出現(xiàn)錯誤。
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Tint) {
Tint tint = (Tint) bean;
tint.setLabel("postProcessAfterInitialization_" + beanName);
}
return bean;
}
}
運行結(jié)果:
- BeanPostProcessor 與 BeanFactoryPostProcessor 的對比
特點 | BeanPostProcessor | BeanFactoryPostProcessor |
---|---|---|
處理目標(biāo) | 主要關(guān)注已經(jīng)實例化的 bean 對象。 | 針對 BeanDefinition,也就是 bean 的元數(shù)據(jù)或者說是配置信息。 |
執(zhí)行時機 | 兩個關(guān)鍵階段: 1. postProcessBeforeInitialization:在 Spring 完成 bean 的實例化、進行屬性注入后,但在 bean 的自定義初始化(如通過@PostConstruct注解定義的方法或init-method方法)之前調(diào)用。 2. postProcessAfterInitialization:在 bean 的自定義初始化方法執(zhí)行后調(diào)用 |
在 BeanDefinition 已經(jīng)完成加載、注冊到 BeanDefinitionRegistry后,但在 bean 實例化之前。這一時期主要處理 bean 的元數(shù)據(jù)。 |
可操作的空間 | 可以操作實例化的 bean 對象,如修改屬性或包裹生成代理對象。 | 主要用于修改或添加 BeanDefinition 的屬性、移除某些 BeanDefinition 等。 |
2. BeanDefinitionRegistryPostProcessor 深入探究
2.1 解讀 BeanDefinitionRegistryPostProcessor
??BeanDefinitionRegistryPostProcessor
是 Spring
容器的一個擴展點,主要用于在 Spring
容器完成對 Bean
的定義信息的加載后、但在它們真正實例化之前,進行額外的操作。
為了更好地理解,讓我們用一個圖書館的類比:
想象一個新的圖書館正在組織其圖書收藏。這個過程可以分為幾個步驟:
-
制定書單:圖書館先列出了所有想要的書的名稱和作者,但還沒有實際購買書籍。在
Spring
中,這就類似于創(chuàng)建BeanDefinition
。 -
在這個步驟后,但在圖書館真正購買書籍之前,假設(shè)圖書館收到了一個特別的捐贈列表(
BeanDefinitionRegistryPostProcessor
)。這個捐贈列表允許圖書館在正式購買書籍之前添加或修改書單。在Spring
中,這是使用BeanDefinitionRegistryPostProcessor
在實際的bean
實例化之前修改bean
定義的時機。 -
按書單采購:此時,圖書館會按照更新后的書單進行購書。這個過程在
Spring
中類似于bean
的實例化和屬性填充。
更佳專業(yè)化的描述如下:
??BeanDefinitionRegistryPostProcessor
是Spring
中的一個高級擴展接口,繼承自 BeanFactoryPostProcessor
。它提供了更為深入的方式來干預(yù)bean
定義的注冊過程。
??這個接口定義于 org.springframework.beans.factory.support
包內(nèi),它的特殊之處在于,除了能夠像 BeanFactoryPostProcessor
那樣修改已經(jīng)注冊的bean
定義(BeanDefinition
),還能向注冊中心 BeanDefinitionRegistry
中動態(tài)地添加或移除bean
定義。
??BeanDefinitionRegistryPostProcessor
提供了一個核心方法:postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
。通過該方法,我們可以直接操作 BeanDefinitionRegistry
,這是一個專門用于bean
定義注冊的中心接口。它允許我們直接注冊新的bean
定義、修改已有的bean
定義或者完全移除某些bean
定義。
??使用這個接口的常見場景包括基于項目的特定條件動態(tài)地注冊beans
,例如,可能只在某些環(huán)境中需要的beans
,或者基于配置選項動態(tài)地選擇實現(xiàn)類。
??與 BeanFactoryPostProcessor
的關(guān)鍵區(qū)別在于其執(zhí)行時機。BeanDefinitionRegistryPostProcessor
的方法在所有其他 BeanFactoryPostProcessor
方法之前執(zhí)行,這確保了它可以在其他處理器操作前先注冊或修改bean
定義。
??總的來說,BeanDefinitionRegistryPostProcessor
提供了一種在Spring
容器配置解析階段動態(tài)介入的能力,允許我們在其他配置處理器介入之前,進行更為深入的bean
定義的調(diào)整和優(yōu)化。
2.2 BeanDefinitionRegistryPostProcessor 的執(zhí)行時機
執(zhí)行時機用一張流程圖表示如下:
-
加載配置:
Spring
從各種來源(如XML
文件、Java
配置、注解)加載配置信息。 -
解析配置: 根據(jù)加載的配置,
Spring
創(chuàng)建對應(yīng)的BeanDefinition
。 -
注冊BeanDefinition: 解析完成后,
Spring
將這些BeanDefinition
對象注冊到BeanDefinitionRegistry
中。 -
執(zhí)行BeanDefinitionRegistryPostProcessor: 這個后置處理器提供了一個重要的擴展點,允許在所有
BeanDefinition
注冊完畢后,但在Bean
實例化之前進行一些操作。例如:注冊新的BeanDefinition
、修改或刪除現(xiàn)有的BeanDefinition
。 -
執(zhí)行BeanFactoryPostProcessor: 這個后置處理器提供了另一個擴展點,它主要允許查看或修改已經(jīng)注冊的
BeanDefinition
。例如,根據(jù)某些條件更改Bean
的作用域或?qū)傩灾怠?/li> -
實例化Bean: 這是將
BeanDefinition
轉(zhuǎn)換為實際的Bean
實例的過程。 -
依賴注入: 在這一步,
Spring
框架會按照BeanDefinition
的描述為bean
實例注入所需的依賴。 -
Bean初始化: 在所有依賴都注入后,特定的初始化方法(如通過
@PostConstruct
指定的)將會被調(diào)用,完成Bean
的最后設(shè)置。 -
執(zhí)行BeanPostProcessor的方法:
BeanPostProcessor
提供了攔截的能力,允許在Bean
初始化階段結(jié)束之前和之后進行操作。 -
Bean完全初始化: 在此階段,
Bean
完全初始化并準(zhǔn)備好被應(yīng)用程序使用。
虛線解釋:
- 執(zhí)行BeanDefinitionRegistryPostProcessor 到 執(zhí)行BeanFactoryPostProcessor 之間的虛線:
??注冊/修改/刪除BeanDefinition
: 在執(zhí)行 BeanDefinitionRegistryPostProcessor
的過程中,除了執(zhí)行已定義的操作外,還提供了一個重要的擴展點,允許我們注冊新的 BeanDefinition
、修改或刪除已有的 BeanDefinition
。這為我們提供了一個機會在后續(xù)的 BeanFactoryPostProcessor
執(zhí)行前改變或增強我們的 bean
定義。
- 執(zhí)行BeanFactoryPostProcessor 到 實例化Bean 之間的虛線:
??查看/修改BeanDefinition
: BeanFactoryPostProcessor
允許我們查看或修改已注冊的 BeanDefinition
。這意味著在 bean
實例化之前,我們還有最后一次機會修改 bean
的定義或?qū)傩?。例如,根?jù)某些運行時環(huán)境或條件更改 bean
的作用域。
2.3. 動態(tài)注冊 Bean:BeanDefinitionRegistryPostProcessor 實踐
??假設(shè)有一個Fruit
的抽象水果類,以及兩個具體的水果類:Apple
和Orange
。在最初,IOC
容器中只注冊了Apple
,沒有Orange
。我們將使用BeanDefinitionRegistryPostProcessor
來注冊一個Orange
的實例,然后利用BeanFactoryPostProcessor
來為所有的Fruit
實例設(shè)置屬性。
- 聲明Bean
首先,我們定義抽象類Fruit
及其屬性:
package com.example.demo.bean;
public abstract class Fruit {
protected String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Apple extends Fruit {
@Override
public String toString() {
return "Apple{" + "type='" + type + '\'' + "}";
}
}
package com.example.demo.bean;
public class Orange extends Fruit {
@Override
public String toString() {
return "Orange{" + "type='" + type + '\'' + '}';
}
}
Orange
類沒有標(biāo)注@Component
注解,Spring
的組件掃描功能默認(rèn)不會為其創(chuàng)建bean
,這個例子中會在OrangeRegisterPostProcessor
里動態(tài)創(chuàng)建。
- 編寫后置處理器
使用后置處理器來注冊Orange
:
package com.example.demo.processor;
import com.example.demo.bean.Orange;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* OrangeRegisterPostProcessor是一個BeanDefinitionRegistryPostProcessor。
* 它的主要作用是檢查IOC容器中是否已經(jīng)包含了名為"orange"的bean定義。
* 如果沒有,它會動態(tài)創(chuàng)建一個Orange類的bean定義并注冊到容器中。
*/
@Component
public class OrangeRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
System.out.println("postProcessBeanDefinitionRegistry in OrangeRegisterPostProcessor started.");
if (!registry.containsBeanDefinition("orange")) {
BeanDefinition orangeDefinition = BeanDefinitionBuilder.genericBeanDefinition(Orange.class).getBeanDefinition();
registry.registerBeanDefinition("orange", orangeDefinition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
System.out.println("postProcessBeanFactory in OrangeRegisterPostProcessor started.");
}
}
為所有的Fruit
實例設(shè)置屬性:
package com.example.demo.processor;
import com.example.demo.bean.Fruit;
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;
/**
* FruitTypeSetterPostProcessor是一個BeanFactoryPostProcessor。
* 它的主要作用是為所有Fruit類型的bean(Apple和Orange)設(shè)置"type"屬性。
* 其中,屬性的值與bean的名稱相同。
*/
@Component
public class FruitTypeSetterPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
System.out.println("postProcessBeanFactory in FruitTypeSetterPostProcessor started.");
String[] fruitNames = beanFactory.getBeanNamesForType(Fruit.class);
for (String name : fruitNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
beanDefinition.getPropertyValues().add("type", name);
}
}
}
- 測試運行
package com.example.demo;
import com.example.demo.bean.Apple;
import com.example.demo.bean.Orange;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
Apple apple = context.getBean(Apple.class);
System.out.println(apple);
Orange orange = context.getBean(Orange.class);
System.out.println(orange);
}
}
運行結(jié)果:
這段代碼展示了如何使用BeanDefinitionRegistryPostProcessor
來動態(tài)地注冊beans
和為其設(shè)置屬性。
3. 三種后置處理器的對比
后置處理器 | 處理目標(biāo) | 執(zhí)行時機 | 主要操作 |
---|---|---|---|
BeanPostProcessor | 針對已經(jīng)實例化但未完全初始化的 bean 對象 | 在bean的初始化過程中,具體是在bean生命周期的初始化方法前后執(zhí)行 | 主要用于在bean初始化前后進行操作,如修改bean屬性、生成代理對象等。 |
BeanFactoryPostProcessor | 主要針對BeanDefinition,即bean的配置元數(shù)據(jù) | 所有的 BeanDefinition 已加載到 BeanDefinitionRegistry 且已完成注冊。此時機允許我們在bean真正實例化和初始化之前,對其配置元數(shù)據(jù)(即 BeanDefinition)進行修改或添加操作 | 修改已注冊的 BeanDefinition。此操作可能包括修改屬性、改變類的定義或者進行其他任何與 BeanDefinition 相關(guān)的操作 |
BeanDefinitionRegistryPostProcessor | 主要針對BeanDefinitionRegistry,該處理器可以處理來自各種配置源(如配置文件、Java配置等)的BeanDefinition | 在所有 BeanDefinition 被加載和注冊之后,但在其他 BeanFactoryPostProcessor 執(zhí)行之前 | 向 BeanDefinitionRegistry 注冊、修改或移除 BeanDefinition |
4. 總結(jié)與洞見
4.1. BeanFactoryPostProcessor 與 BeanPostProcessor 的差異
??BeanFactoryPostProcessor
和 BeanPostProcessor
都是 Spring
框架中為了增強容器的處理能力而提供的擴展點。它們都可以對 Bean
進行定制化處理,但它們的關(guān)注點和應(yīng)用時機不同。
- BeanFactoryPostProcessor:
-
功能: 允許我們在
Spring
容器實例化任何bean
之前讀取bean
的定義(bean
的元數(shù)據(jù))并進行修改。 -
作用時機: 它會在
BeanFactory
的標(biāo)準(zhǔn)初始化之后被調(diào)用,此時,所有的bean
定義已經(jīng)被加載到容器中,但還沒有實例化任何bean
。此時我們可以添加、修改或移除某些bean
的定義。 -
常見應(yīng)用: 動態(tài)修改
bean
的屬性、改變bean
的作用域、動態(tài)注冊新的bean
等。 -
示例接口方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
;
- BeanPostProcessor:
-
功能: 允許我們在
Spring
容器實例化bean
之后對bean
進行處理,提供了一種機會在bean
的初始化前后插入我們的自定義邏輯。 -
作用時機: 它在
bean
的生命周期中的兩個時間點被調(diào)用,即在自定義初始化方法(如@PostConstruct
,init-method
)之前和之后。 -
常見應(yīng)用: 對特定的
bean
實例進行一些額外處理,如進行某種代理、修改bean
的狀態(tài)等。 -
示例接口方法:
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
; 和Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
;
總結(jié):
-
BeanFactoryPostProcessor
主要關(guān)注于整個容器的配置,允許我們修改bean
的定義或元數(shù)據(jù)。它是容器級別的。 -
BeanPostProcessor
主要關(guān)注于bean
的實例,允許我們在初始化前后對bean
實例進行操作。它是bean
級別的。
4.2. BeanFactoryPostProcessor 與 BeanDefinitionRegistryPostProcessor 的關(guān)系
??BeanFactoryPostProcessor
和 BeanDefinitionRegistryPostProcessor
都是 Spring
中提供的兩個重要的擴展點,它們都允許我們在 Spring
容器啟動過程中對 Bean
的定義進行定制處理。但它們的應(yīng)用時機和功能上存在一些不同。
- BeanFactoryPostProcessor:
-
功能: 允許我們在
Spring
容器實例化任何bean
之前讀取bean
的定義 (BeanDefinition
) 并進行修改。 -
作用時機: 在所有的
bean
定義都被加載、但bean
實例還未創(chuàng)建的時候執(zhí)行。 -
常見應(yīng)用: 修改已加載到容器中的
bean
定義的屬性,例如更改某個bean
的作用域、屬性值等。 -
主要方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
;
- BeanDefinitionRegistryPostProcessor:
-
功能: 擴展了
BeanFactoryPostProcessor
,提供了一個新的方法來修改應(yīng)用程序的上下文的bean
定義。此外,還可以動態(tài)注冊新的bean
定義。 -
作用時機: 它也是在所有
bean
定義被加載后執(zhí)行,但在BeanFactoryPostProcessor
之前。 -
常見應(yīng)用: 動態(tài)注冊新的
bean
定義、修改或移除已有的bean
定義。 -
主要方法:
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException
;
總結(jié):
-
BeanFactoryPostProcessor
主要是用來修改已經(jīng)定義的bean
定義,而不是注冊新的bean
。 -
BeanDefinitionRegistryPostProcessor
是BeanFactoryPostProcessor
的擴展,并提供了額外的能力來動態(tài)地注冊、修改、移除bean
定義。文章來源:http://www.zghlxwxcb.cn/news/detail-666378.html
在 Spring
容器的啟動過程中,首先執(zhí)行的是 BeanDefinitionRegistryPostProcessor
的方法,之后才是 BeanFactoryPostProcessor
的方法。文章來源地址http://www.zghlxwxcb.cn/news/detail-666378.html
歡迎一鍵三連~
有問題請留言,大家一起探討學(xué)習(xí)
----------------------Talk is cheap, show me the code-----------------------
到了這里,關(guān)于Spring高手之路13——BeanFactoryPostProcessor與BeanDefinitionRegistryPostProcessor解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!