Springboot容器級后置處理器BeanDefinitionRegistryPostProcessor
前言
通過這篇文章來大家分享一下,另外一個Springboot的擴(kuò)展點(diǎn)BeanDefinitionRegistryPostProcessor,一般稱這類擴(kuò)展點(diǎn)為容器級后置處理器,另外一類是Bean級的后置處理器;容器級的后置處理器會在Spring容器初始化后、刷新前這個時間執(zhí)行一次,Bean級的重置處理器,則是在每一個Bean實(shí)例化前后都會執(zhí)行。
1. 功能特性
postProcessBeanDefinitionRegistry()方法可以通過BeanDefinitionRegistry對BeanDefintion進(jìn)行增刪改查;
繼承了BeanFactoryPostProcessor,BeanFactoryPostProcessor是容器級別的擴(kuò)展接口,org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory方法在容器實(shí)例化后、刷新容器前被執(zhí)行,即在容器刷新前還可以對BeanDefintion再作一些操作;
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
@FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
總結(jié)起來就是,在所有的BeanDefinition加載完成之后,Bean真正被實(shí)例化之前,可以通過實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口,對BeanDefinition再做一些定制化的操作,比如修改某個bean的BeanDefinition的屬性、手動注冊一些復(fù)雜的Bean。
對于Spring原理不太熟悉的小伙伴心里看到這可能有點(diǎn)暈了,BeanDefinition是什么?BeanDefinitionRegistry又是什么?ConfigurableListableBeanFactory又又是什么?別著急,這里拐個彎簡單的解釋一下,對整篇文章的理解會更順暢。
1.1 BeanDefinition
大家都知道,Spring的核心之一是IOC(控制反轉(zhuǎn)),Spring之所以可以實(shí)現(xiàn)bean控制權(quán)的反轉(zhuǎn),是因?yàn)镾pring的容器功能,在bean納入Spring容器管理前,所有bean會被抽象封裝成一個BeanDefinition實(shí)例,然后會在不同的時機(jī)根據(jù)BeanDefinition實(shí)例信息對bean進(jìn)行實(shí)例化。
簡單說,Dog.java描述狗這一類動物的屬性和行為,BeanDefinition描述Dog.java這個類。
1.2 BeanDefinitionRegistry
BeanDefinitionRegistry從字面意思看是bean的定義信息的注冊登記,其實(shí)這個類的功能和字面意思一樣,就是對BeanDefinition進(jìn)行管理(增刪改查);
public interface BeanDefinitionRegistry extends AliasRegistry { //注冊beanDefinition void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; //移除指定的beanDefinition void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; //根據(jù)beanName查詢beanDefinition BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; //判斷某個beanDefinition是否已經(jīng)注冊 boolean containsBeanDefinition(String beanName); //獲取所有已注冊的beanDefinition String[] getBeanDefinitionNames(); //獲取所有已注冊的beanDefinition的數(shù)量 int getBeanDefinitionCount(); //判斷某個beanDefinition是否已經(jīng)被使用 boolean isBeanNameInUse(String beanName); }
1.3 ConfigurableListableBeanFactory
上面提到了Spring的容器,Spring的核心之一是IOC,那么Spring的容器設(shè)計就是核心中的核心了。Spring的容器有多種形態(tài),最基礎(chǔ)的形態(tài)就是BeanFactory,ConfigurableListableBeanFactory間接繼承了BeanFactory,因此ConfigurableListableBeanFactory實(shí)現(xiàn)類除了有Spring基礎(chǔ)版本容器的功能外,還有一些高級的功能,Springboot默認(rèn)的實(shí)際實(shí)現(xiàn)是DefaultListableBeanFactory,有興趣的小伙伴可以以此為入口深入探究一番,這里不展開細(xì)說了。
2.自定義實(shí)現(xiàn)
2.1 MyBeanDefinitionRegistryPostProcessor
下面通過一個具體類MyBeanDefinitionRegistryPostProcessor實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口,來探究BeanDefinitionRegistryPostProcessor實(shí)現(xiàn)類的初始化和執(zhí)行過程。
- 在postProcessBeanDefinitionRegistry()方法被調(diào)用的時候手工在Spring中注冊了Dog類的BeanDefinition信息;
- 在postProcessBeanFactory()方法被調(diào)用的時候,從Spring容器中取出Dog類的BeanDefinition信息和Dog類的實(shí)例;
@Data public class Dog { private String name; private String color; }
@Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { //手工定義一個beanDefinition實(shí)例 RootBeanDefinition beanDefinition = new RootBeanDefinition(); //給beanDefinition填充屬性 beanDefinition.setBeanClass(Dog.class); MutablePropertyValues propertyValues = new MutablePropertyValues(); PropertyValue propertyValue1 = new PropertyValue("name", "旺財"); PropertyValue propertyValue2 = new PropertyValue("color", "黑色"); propertyValues.addPropertyValue(propertyValue1); propertyValues.addPropertyValue(propertyValue2); beanDefinition.setPropertyValues(propertyValues); //注冊手工定義的beanDefinition registry.registerBeanDefinition("dog", beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("-----------start------------"); //根據(jù)類名取出手工注冊的beanDefinition BeanDefinition beanDefinition = beanFactory.getBeanDefinition("dog"); System.out.println(beanDefinition.getBeanClassName()); //根據(jù)類從容器中取出手工注冊的beanDefinition所描述的實(shí)例bean Dog dog = beanFactory.getBean(Dog.class); System.out.println(dog.getName()); System.out.println(dog.getColor()); System.out.println("-----------end------------"); } }
單元測試
@Test public void test(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu"); Dog dog = ((Dog) context.getBean("dog")); System.out.println(dog.getName()); System.out.println(dog.getColor()); }
2.2 UML類圖
通過BeanDefinitionRegistryPostProcessorUML類圖可以看出BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor,postProcessBeanDefinitionRegistry()方法屬于BeanDefinitionRegistryPostProcessor,postProcessBeanFactory()屬于BeanFactoryPostProcessor,所有實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口的實(shí)現(xiàn)類都需要實(shí)現(xiàn)這兩個方法,而作為Springboot的擴(kuò)展點(diǎn)之一,其擴(kuò)展的邏輯也在這兩個方法中。
3. 初始化和執(zhí)行時機(jī)
通過自定義的MyBeanDefinitionRegistryPostProcessor類,實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口,從項(xiàng)目啟動開始,其執(zhí)行過程如下:
- 執(zhí)行項(xiàng)目的主類,org.springframework.boot.SpringApplication#run被調(diào)用;
- 進(jìn)入boot.SpringApplication#run方法后,剛開始是一些Spring容器初始化的配置操作,直到執(zhí)行到org.springframework.boot.SpringApplication#refreshContext,開始容器刷新,進(jìn)入了關(guān)鍵階段;
- 在SpringApplication#refreshContext,實(shí)際的刷新邏輯是在org.springframework.context.support.AbstractApplicationContext#refresh方法中;
- AbstractApplicationContext#refresh方法中,調(diào)用org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors開始初始化和執(zhí)行實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry()和postProcessBeanFactory();
- 進(jìn)入AbstractApplicationContext#invokeBeanFactoryPostProcessors方法,發(fā)現(xiàn)又調(diào)用了org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors();
- 在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法中,并不是直接就初始化和執(zhí)行postProcessBeanDefinitionRegistry()和postProcessBeanFactory(),而是又進(jìn)行了一系列的判斷,其判斷順序是:
1、通過AbstractApplicationContext#addBeanFactoryPostProcessor提前注冊的BeanDefinitionRegistryPostProcessor實(shí)現(xiàn)類;
2、實(shí)現(xiàn)了PriorityOrdered接口;
3、是否實(shí)現(xiàn)了Ordered;
4、剩下的其他BeanDefinitionRegistryPostProcessor實(shí)現(xiàn)類;自定義的MyBeanDefinitionRegistryPostProcessor就屬于第4類,所以是所有實(shí)現(xiàn)里較晚才被執(zhí)行的,如果想要提前被執(zhí)行,可以考慮前面三種方式;
- 在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法中執(zhí)行完MyBeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法后,緊接著就開始執(zhí)行MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory方法了;從整個調(diào)用過程看postProcessBeanDefinitionRegistry()是早于postProcessBeanFactory()方法執(zhí)行;
下面是我根據(jù)整個調(diào)用過程畫的一個時序圖,過程確實(shí)比較復(fù)雜,但是邏輯比較清晰,因此并不難理解,想要真的搞清楚整個過程,最好的方法就是照著這個圖,親自執(zhí)行一遍,通過debug觀察每一個關(guān)鍵節(jié)點(diǎn)的執(zhí)行過程。
4. 內(nèi)部實(shí)現(xiàn)類
spring-boot-starter-web中內(nèi)置的實(shí)現(xiàn)類有CachingMetadataReaderFactoryPostProcessor、ConfigurationClassPostProcessor、ConfigurationWarningsPostProcessor、EmbeddedDataSourceBeanFactoryPostProcessor、ImportsCleanupPostProcessor、TestRestTemplateRegistrar、WebTestClientRegistrar、WsdlDefinitionBeanFactoryPostProcessor,觀察一下每個實(shí)現(xiàn)類會發(fā)現(xiàn):都比較類似,這些內(nèi)置實(shí)現(xiàn)類都是Springboot中的內(nèi)部類,通過這些BeanDefinitionRegistryPostProcessor內(nèi)部實(shí)現(xiàn)類向Spring容器中注冊了一些特殊的BeanDefinition,如果展開詳細(xì)再說一說這些Bean,怕是一天一夜也說不完,有興趣的小伙伴可以深入了解一下,這里就不再展開了。
5. 總結(jié)
通過梳理整個過程,其實(shí)最關(guān)鍵的就是一句話:在Spring容器初始后、未刷新前,即Bean已被掃描注冊為BeanDefinition后,未正式實(shí)例化前,可以通過實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor做一些額外的操作。
到此這篇關(guān)于Springboot容器級后置處理器BeanDefinitionRegistryPostProcessor的文章就介紹到這了,更多相關(guān)Springboot BeanDefinitionRegistryPostProcessor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中@RestControllerAdvice @ExceptionHandler異常統(tǒng)一處
這篇文章主要介紹了SpringBoot中@RestControllerAdvice @ExceptionHandler異常統(tǒng)一處理類失效原因,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01淺析Java中關(guān)鍵詞volatile底層的實(shí)現(xiàn)原理
在 Java 并發(fā)編程中,有 3 個最常用的關(guān)鍵字:synchronized、ReentrantLock 和 volatile,這篇文章主要來和大家聊聊volatile底層的實(shí)現(xiàn)原理,感興趣的可以了解下2024-02-02@Schedule?如何解決定時任務(wù)推遲執(zhí)行
這篇文章主要介紹了@Schedule?如何解決定時任務(wù)推遲執(zhí)行問題。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09解決springboot項(xiàng)目找不到resources目錄下的資源問題
這篇文章主要介紹了解決springboot項(xiàng)目找不到resources目錄下的資源問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot 錯誤處理機(jī)制與自定義錯誤處理實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringBoot 錯誤處理機(jī)制與自定義錯誤處理實(shí)現(xiàn)詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11springboot+thymeleaf+layui的實(shí)現(xiàn)示例
本文主要介紹了springboot+thymeleaf+layui的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12