結束了前面的基礎結構分析,瞭解到 Spring
是如何識別配置文件和進行解析屬性,最終將 bean
加載到內存中。同時爲了更好得理解 Spring
的擴展功能,咱們先來鞏固一下 beanFactory
和 bean
的概念,而後再分析新內容後處理器 PostProcessor
。html
首先咱們先將 Spring
想像成一個大容器,而後保存了不少 bean
的信息,根據定義:bean
是一個被實例化,組裝,並經過 Spring IoC
容器所管理的對象,也能夠簡單得理解爲咱們在配置文件配置好元數據,Spring IoC
容器會幫咱們對 bean
進行管理,這些對象在=使用的時候經過 Spring
取出就能使用。java
那麼是誰幫這個 Spring
管理呢,那就是 BeanFactory
,粗暴點直譯爲 bean
工廠,但其實它纔是承擔容器功能的幕後實現者,它是一個接口,提供了獲取 bean
、獲取別名 Alias
、判斷單例、類型是否匹配、是否原型等方法定義,因此須要經過引用,實現具體方法才後才能使用。git
回顧完 beanFactory
後,咱們再來回顧在前面內容中,看到過不少後處理器 PostProcessor
的代碼影子,分別是 BeanFactoryPostProcessor
:主體是 BeanFactory
, 和 BeanPostProcessor
:主體是 Bean
,這二者都是 Spring
用來爲使用者提供的擴展功能之一。github
接下來爲了更好的分析和了解使用後處理器,實現擴展功能,一塊兒跟蹤源碼學習吧~spring
BeanFactoryPostProcessor
是一個接口,在裏面只有一個方法定義:數組
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
複製代碼
定義:是 Spring
對外提供可擴展的接口,可以在容器加載了全部 bean
信息(AbstractApplicationContext#obtainFreshBeanFactory
方法)以後,bean
實例化以前執行,用來修改 bean
的定義屬性。緩存
能夠看到,方法參數是 ConfigurableListableBeanFactory beanFactory
,說明咱們能夠經過引用該接口,在方法中實現邏輯,對容器中 bean
的定義(配置元數據)進行處理mvc
同時,執行後處理器是有前後順序的概念,咱們能夠經過設置 order
屬性來控制它們的執行次序,前提是 BeanFactoryPostProcessor
實現了 Order
接口。app
下面一塊兒來看下它的如何使用,以及是如何進行註冊和執行~ide
這個類是 Spring
容器裏自帶的後處理器,是用來替換佔位符,填充屬性到 bean
中。
像咱們在 xml
文件中配置了屬性值爲 ${max.threads}
,可以經過它來找到 max.threads
在配置文件對應的值,而後將屬性填充到 bean
中。
雖然在 Spring 5 中,PropertyPlaceholderConfigurer
已經打上了不建議使用的標誌 @Deprecated
,看了文件註釋,提示咱們去使用 Environment
來設置屬性,但我以爲這個後處理器的思想是同樣的,因此仍是拿它做爲例子進行熟悉。
先來看下它的繼承體系:
忽略它被冷落的下劃線標籤
當 Spring
加載任何實現了 BeanFactoryPostProcessor
接口的 bean
配置時,都會在 bean
工廠載入全部 bean
的配置以後執行 postProcessBeanFactory
方法。
能夠看到它引用了 BeanFactoryPostProcessor
接口,在 PropertyResourceConfigurer
父類中實現了 postProcessBeanFactory
,在方法中依次調用了合併資源 mergedProps
方法,屬性轉換 convertProperties
方法和真正修改 beanFactory
中配置元數據的 processProperties(beanFactory, mergedProps)
方法。
由於經過在 PropertyPlaceholderConfigurer
的後處理方法 postProcessBeanFactory
,BeanFactory
在實例化任何 bean
以前得到配置信息,從而可以正確解析 bean
描述文件中的變量引用。
因此經過後處理器,咱們可以對 beanFactory
中的 bean
配置信息在實例化前還有機會進行修改。
題外話:想到以前我遇到全半角空格的配置問題,程序認爲全半角空格不是同一個字符,但肉眼卻很難察覺,因此感受能夠在加載配置信息時,經過自定義一個後處理,在實例化以前,將全角空格轉成半角空格,這樣程序比較時都變成統同樣式。因此後處理器提供的擴展功能可讓咱們對想要處理的 bean
配置信息進行特定修改
實現的功能與書中的相似,例如以前西安奔馳汽車維權事件,若是相關網站想要屏蔽這奔馳這兩個字,能夠經過後處理器進行替換:
1. 配置文件 factory-post-processor.xml
<bean id="carPostProcessor" class="context.CarBeanFactoryPostProcessor">
<property name="obscenties">
<!--set 屬性-->
<set>
<value>奔馳</value>
<value>特斯拉</value>
</set>
</property>
</bean>
<bean id="car" class="base.factory.bean.Car">
<property name="price" value="10000"/>
<property name="brand" value="奔馳"/>
</bean>
複製代碼
2. 後處理器 CarBeanFactoryPostProcessor
public class CarBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
/** * 敏感詞 */
private Set<String> obscenties;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 從 beanFactory 中獲取 bean 名字列表
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
StringValueResolver valueResolver = strVal -> {
if (isObscene(strVal)) return "*****";
return strVal;
};
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
// 這一步纔是真正處理 bean 的配置信息
visitor.visitBeanDefinition(definition);
}
}
/** * 判斷 value 是否在敏感詞列表中 * @param value 值 * @return boolean */
private boolean isObscene(Object value) {
String potentialObscenity = value.toString().toUpperCase();
return this.obscenties.contains(potentialObscenity);
}
}
複製代碼
3. 啓動
public class BeanFactoryPostProcessorBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/factory-post-processor.xml");
// 這兩行其實能夠不寫,由於在 refresh() 方法中,調用了一個函數將後處理器執行了,具體請往下看~
BeanFactoryPostProcessor beanFactoryPostProcessor = (BeanFactoryPostProcessor) context.getBean("carPostProcessor");
beanFactoryPostProcessor.postProcessBeanFactory(context.getBeanFactory());
// 輸出 :Car{maxSpeed=0, brand='*****', price=10000.0},敏感詞被替換了
System.out.println(context.getBean("car"));
}
}
複製代碼
經過上面的演示代碼,新增一個自定義實現 BeanFactoryPostProcessor
的後處理器 CarBeanFactoryPostProcessor
,在 postProcessBeanFactory
方法中進行邏輯處理,最後經過 visitor.visitBeanDefinition
修改配置信息。
查看輸出結果,能發現寶馬敏感詞已經被屏蔽了,實現了後處理器的邏輯功能~
按照通常套路,後處理器須要有個地方進行註冊,而後才能進行執行,經過代碼分析,的確在 AbstractApplicationContext
中看到了 beanFactoryPostProcessors
數組列表,但往數組中添加後處理器的方法 addBeanFactoryPostProcessor
只在單元測試包調用了。
這讓我很迷惑它究竟是在哪裏進行註冊,直到我看到它的執行方法,原來咱們定義的後處理器在 bean
信息加載時就放入註冊表中,而後經過 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false)
方法獲取後處理器列表遍歷執行。
因此前面的 beanFactoryPostProcessors
數組列表,是讓咱們經過硬編碼方法方式,手動添加進去,而後經過 context.refresh()
方法後,再執行硬編碼的後處理器
例以下面這個例子
public class HardCodeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("Hard Code BeanFactory Post Processor execute time");
}
}
// 硬編碼 後處理器執行時間
BeanFactoryPostProcessor hardCodeBeanFactoryPostProcessor = new HardCodeBeanFactoryPostProcessor();
context.addBeanFactoryPostProcessor(hardCodeBeanFactoryPostProcessor);
// 更新上下文
context.refresh();
// 輸出:
//Hard Code BeanFactory Post Processor execute time
//Car{maxSpeed=0, brand='*****', price=10000.0}
System.out.println(context.getBean("car"));
複製代碼
看完了怎麼使用後,咱們來分析下 Spring
是如何識別和執行 BeanFactoryPostProcessor
,入口方法:
org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
實際上,委派了 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
代理進行執行。因爲代碼有點長,因此我畫了一個流程圖,能夠結合流程圖來分析代碼:
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// beanFactory 默認使用的是 DefaultListableBeanFactory,屬於 BeanDefinitionRegistry
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 兩個後處理器列表
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 硬編碼註冊的後處理器
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 分類處理
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 首先,調用實現 priorityOrder 的 beanDefinition 這就是前面提到過的優先級概念,這一步跟下面的優先級不同之處,這一步的優先級是帶有權重
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();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement 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);
// 執行 definitionRegistryPostProcessor 接口的方法 :postProcessBeanDefinitionRegistry
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.最後,調用全部其餘後處理器,直到再也不出現其餘 bean 爲止
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();
}
// 執行 postProcessBeanFactory 回調方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 不要在這裏初始化 factoryBean:咱們須要保留全部常規 bean 未初始化,以便讓 bean 工廠後處理程序應用於它們
// 註釋 6.4 在這個步驟中,咱們自定義的 carBeanFactoryPostProcessor 才真正註冊並執行
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);.
// 跳過度類的邏輯
// 首先執行的是帶有權重順序的後處理器
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 下一步執行普通順序的後處理器
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最後執行的是普通的後處理器
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除緩存的合併bean定義,由於後處理程序可能已經修改了原始元數據,例如替換值中的佔位符
beanFactory.clearMetadataCache();
}
複製代碼
從上面貼的代碼中可以看到,對於 beanFactoryPostProcessor
的處理主要分兩種狀況:
beanFactory
是 BeanDefinitionRegistry
類型:須要特殊處理beanFactory
不是 BeanDefinitionRegistry
類型:進行普通處理對於每種狀況都須要考慮硬編碼注入註冊的後處理器(上面已經提到如何進行硬編碼)以及經過配置注入的後處理器
對於 BeanDefinitionRegistry
類型的處理器的處理主要包括如下內容:
List
registryPostProcessors
:記錄經過硬編碼方式註冊的 BeanDefinitionRegistryPostProcessor
regularPostProcessors
:記錄經過硬編碼方式註冊的 BeanFactoryPostProcessor
regitstryPostProcessorBeans
:記錄經過配置方式註冊的 BeanDefinitionRegistryPostProcessor
BeanFactoryPostProcessor
的 postProcessBeanFactory
方法beanFactoryPostProcessors
中非 BeanDefinitionRegistryPostProcessor
類型的後處理器進行統一的 postProcessBeanFactory
方法beanFactory
處理:其實在這一步中,就是忽略了 BeanDefinitionRegistryPostProcessor
類型,對 BeanFactoryPostProcessor
進行直接處理。流程圖中描述了總體調用鏈路,具體調用方法在代碼中的註釋也描述出來了,因此結合起來看應該可以理解總體流程~
本次分析了 beanFactory
的後處理器 BeanFactoryPostProcessor
,瞭解了 Spring
給咱們提供的這個擴展接口使用用途和在源碼中如何進行激活執行。
因爲我的技術有限,若是有理解不到位或者錯誤的地方,請留下評論,我會根據朋友們的建議進行修正
由於我也是一邊看書,一邊作筆記,下載了源碼。 代碼和註釋都在裏面,小夥伴們能夠下載我上傳的代碼,親測可運行~
spring-analysis-note 碼雲 Gitee 地址
spring-analysis-note Github 地址