Spring BeanPostProcessor源碼示例解析
正文
BeanPostProcessor對(duì)于Spring開發(fā)者來說一定不陌生,平時(shí)開發(fā)過程中如果涉及對(duì)中間件做增強(qiáng)處理或者需要對(duì)某個(gè)實(shí)際Bean做初始化前的處理,一般都可以使用該接口來實(shí)現(xiàn)。
對(duì)于自己來說自己在對(duì)接RocketMQ、RabbitMQ過程中需要實(shí)現(xiàn)一個(gè)快速接入SpringBoot的中間插件,最快速的就是實(shí)現(xiàn)該接口對(duì)中間件中的Bean做增強(qiáng)處理,能夠快速接入SpringBoot。
1. BeanPostProcessor介紹
BeanPostProcessor是一個(gè)接口,該接口提供給開發(fā)者使用,開發(fā)者可以實(shí)現(xiàn)該接口,然后在實(shí)現(xiàn)類中對(duì)想要處理的類做增強(qiáng)處理。
該接口中提供了兩個(gè)基礎(chǔ)方法:
- postProcessBeforeInitialization:Bean初始化前對(duì)Bean的增強(qiáng)處理
- postProcessAfterInitialization:Bean初始化后對(duì)Bean的增強(qiáng)處理
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; } }
下面舉一個(gè)最簡(jiǎn)單的例子來看一下BeanPostProcessor的使用。
2. BeanPostProcessor的使用
@Service public class UserService { private String name = "陳tom"; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
@Component public class MyPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("userService".equals(beanName)) { UserService userService = (UserService) bean; userService.setName("陳湯姆"); userService.setAge(21); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
@SpringBootApplication public class BootApplication { @Resource private DefaultListableBeanFactory defaultListableBeanFactory; public static void main(String[] args) { SpringApplication.run(BootApplication.class,args); } @PostConstruct public void init() { UserService userService = defaultListableBeanFactory.getBean("userService", UserService.class); System.out.println("測(cè)試結(jié)果:姓名:"+userService.getName()+";年齡:"+userService.getAge()); } }
以上的實(shí)現(xiàn)中做了最簡(jiǎn)單的實(shí)現(xiàn),通過MyPostProcessor實(shí)現(xiàn)BeanPostProcessor接口,然后在前置增強(qiáng)處理中加入對(duì)Bean的修改。
然后通過PostConstruct在Bean初始化后做Bean的查詢,得到通過BeanPostProcessor操作的結(jié)果。
所以BeanPostProcessor在使用上來說很簡(jiǎn)單,對(duì)于開發(fā)者來說它很方便的提供了對(duì)于Bean對(duì)象的修改操作,提高了Spring的擴(kuò)展性和靈活性。
3. BeanPostProcessor的作用
說完了最基礎(chǔ)的BeanPostProcessor的使用,通過以上的實(shí)現(xiàn)來梳理下BeanPostProcessor的作用。
- 對(duì)Bean的修改操作,通過實(shí)現(xiàn)該接口可以很快的實(shí)現(xiàn)對(duì)Bean的操作
- 提供前置和后置兩個(gè)處理,提供開發(fā)者可以在初始化前做處理以及初始化后做處理
- 對(duì)于Spring框架的靈活性,使用Spring框架也可以很方便的做自己的邏輯處理
自己對(duì)于BeanPostProcessor最大的感受就是太靈活了,開發(fā)者只要實(shí)現(xiàn)該接口,然后重寫其中的方法就可以實(shí)現(xiàn)自己的邏輯。這樣也保證了Spring容器對(duì)于開發(fā)者來說并不是一個(gè)黑盒,開發(fā)者可以通過Spring提供的鑰匙讓Spring容器暴露在開發(fā)者眼中,想要做什么處理都可以根據(jù)提供的鑰匙實(shí)現(xiàn)。(這里其實(shí)還有一個(gè)配套的BeanFactoryPostProcessor,面試的時(shí)候也經(jīng)常被問到BeanPostProcessor和BeanFactoryPostProcessor的區(qū)別,有興趣的可以自己了解下,區(qū)別只是最終服務(wù)的對(duì)象不一樣)
這里不得不說Spring YYDS?。?!
4. BeanPostProcessor注冊(cè)
Q:既然開發(fā)者通過自己的實(shí)現(xiàn)類實(shí)現(xiàn)了BeanPostProcessor,那么Spring又是如何將開發(fā)者做的實(shí)現(xiàn)類加入到Spring中并且執(zhí)行開發(fā)者重寫的前置和后置增強(qiáng)邏輯呢?
這里就要提到Spring對(duì)于BeanPostProcessor的設(shè)計(jì),在Spring中初始化了一個(gè)CopyOnWriteArrayList作為存儲(chǔ)所有實(shí)現(xiàn)BeanPostProcessor的列表。
從實(shí)現(xiàn)上可以看到該列表是一個(gè)寫復(fù)制的列表,通俗點(diǎn)講就是向該列表中添加元素時(shí),不直接往當(dāng)前容器添加,而是先將當(dāng)前容器進(jìn)行Copy,復(fù)制出一個(gè)新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。這里不展開說只要知道就是一個(gè)列表就可以。
Spring向該容器中添加的邏輯源碼如下:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { //存儲(chǔ)BeanPostProcessor列表 private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>(); @Override public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); // Remove from old position, if any this.beanPostProcessors.remove(beanPostProcessor); // Track whether it is instantiation/destruction aware if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { this.hasInstantiationAwareBeanPostProcessors = true; } if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { this.hasDestructionAwareBeanPostProcessors = true; } // 將BeanPostProcessor加入beanPostProcessors列表中 this.beanPostProcessors.add(beanPostProcessor); } }
這里展示的只是最終寫入的邏輯,那么從源頭到寫入BeanPostProcessor的邏輯是怎么樣呢?
以ClassPatchXmlApplicationContext為例:
- org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext
- org.springframework.context.support.AbstractApplicationContext#refresh
- org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
- org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanPostProcessor>)
- org.springframework.beans.factory.support.AbstractBeanFactory#addBeanPostProcessor
時(shí)序圖如下:
5. BeanPostProcessor調(diào)用
說完如何注冊(cè)BeanPostProcessor,最后來說如何將注冊(cè)到beanPostProcessors的實(shí)現(xiàn)類進(jìn)行調(diào)用。
前一篇聊Spring容器時(shí)也提到了Spring在初始化Bean時(shí)會(huì)執(zhí)行BeanPostProcessor的增強(qiáng)處理,具體的調(diào)用源碼如下:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { //初始化Bean protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //調(diào)用BeanPostProcesso#postProcessBeforeInitialization:前置處理 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //執(zhí)行初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //調(diào)用調(diào)用BeanPostProcesso#postProcessAfterInitialization:后置處理 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } //BeanPostProcessor前置邏輯處理 @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { //執(zhí)行實(shí)現(xiàn)BeanPostProcessor的實(shí)現(xiàn)類的前置增強(qiáng)邏輯 Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } //BeanPostProcessor后置邏輯處理 @Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { //執(zhí)行實(shí)現(xiàn)BeanPostProcessor的實(shí)現(xiàn)類的后置增強(qiáng)邏輯 Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } }
從以上源碼中可以看到在初始化Bean時(shí)調(diào)用前置和后置增強(qiáng),并且這里也做了明確的邊界,前置增強(qiáng)處理一定是在invokeInitMethods之前,后置增強(qiáng)一定是在invokeInitMethods之后。這樣就保證了BeanPostProcessor的執(zhí)行順序,也可以讓開發(fā)者在使用過程中只要掌握Bean的生命周期,那么就可以自主的對(duì)Bean進(jìn)行增強(qiáng)處理。
6. 總結(jié)
以上是對(duì)Spring中BeanPostProcessor的源碼梳理,對(duì)BeanPostProcessor的介紹到使用、作用、注冊(cè)實(shí)現(xiàn)和調(diào)用實(shí)現(xiàn),完成了對(duì)BeanPostProcessor的源碼分析。
對(duì)于文中提到的BeanFactoryPostProcessor有興趣的可以從源碼按照這個(gè)過程梳理一下,其實(shí)跟BeanPostProcessor作用是相同的。
以上就是Spring BeanPostProcessor源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于Spring BeanPostProcessor的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
圖解Spring框架的設(shè)計(jì)理念與設(shè)計(jì)模式
這篇文章主要通過多圖詳細(xì)解釋Spring框架的設(shè)計(jì)理念與設(shè)計(jì)模式,需要的朋友可以參考下2015-08-08Java7到Java17之Switch語句進(jìn)化史示例詳解
這篇文章主要為大家介紹了Java7到Java17之Switch語句進(jìn)化史示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Spring Boot非Web項(xiàng)目運(yùn)行配置的方法教程
這篇文章主要介紹了Spring Boot非Web項(xiàng)目運(yùn)行配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09淺談Java中GuavaCache返回Null的注意事項(xiàng)
Guava在實(shí)際的Java后端項(xiàng)目中應(yīng)用的場(chǎng)景還是比較多的,比如限流,緩存,容器操作之類的,本文主要介紹了GuavaCache返回Null的注意事項(xiàng),感興趣的可以了解一下2021-10-10Java如何根據(jù)實(shí)體指定字段值對(duì)其List進(jìn)行排序詳解
在Java項(xiàng)目中可能會(huì)遇到給出一些條件,將List元素按照給定條件進(jìn)行排序的情況,這篇文章主要給大家介紹了關(guān)于Java如何根據(jù)實(shí)體指定字段值對(duì)其List進(jìn)行排序的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07使用maven插件對(duì)java工程進(jìn)行打包過程解析
這篇文章主要介紹了使用maven插件對(duì)java工程進(jìn)行打包過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Servlet實(shí)現(xiàn)簡(jiǎn)單的用戶登錄功能實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于利用Servlet實(shí)現(xiàn)簡(jiǎn)單的用戶登錄功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java鏈表元素查找實(shí)現(xiàn)原理實(shí)例解析
這篇文章主要介紹了Java鏈表元素查找實(shí)現(xiàn)原理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07