Springboot(Spring)的擴展點其實有很多,但是都有一個共同點,都是圍繞著Bean和BeanFactory(容器)展開的,其實這也很好理解,Spring的核心是控制反轉、依賴注入、面向切面編程,再拋開所有的枝枝節節,你發現了什么?Spring提供了一個容器,來管理Bean,整個生態好像是都圍繞這個展開。研究源碼意義,一方面是在于技術本身,另一方面也在于理解接受其中的思想。
沒有目的的亂走總是會迷路,有了目標就不一樣了,所以這篇文章是圍繞以下幾個問題展開的,這也是我想和大家分享的內容:(如果你和我的疑問一樣,關注,收藏+點贊,不迷路哦)
1、BeanPostProcessor接口的功能特性是什么樣的?
2、BeanPostProcessor接口怎么實現擴展?
3、BeanPostProcessor接口的實現類的工作原理是什么?
4、BeanPostProcessor接口的應用場景有哪些?
1、BeanPostProcessor是Bean級別的擴展接口,在Spring管理的Bean實例化完成后,預留了兩種擴展點;
2、這兩處擴展的實現方式就是實現BeanPostProcessor接口,并將實現類注冊到Spring容器中;
3、兩種擴展點分別是BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法;
4、postProcessBeforeInitialization方法的執行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;
5、postProcessAfterInitialization方法的執行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之后;
6、BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,在在Spring管理的每個bean初始化后都會執行到;
1、定義一個實體類Dog,并實現InitializingBean接口,并且實現afterPropertiesSet()。其中afterPropertiesSet()和init()是為了演示BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執行時機;
@Getter@Setter@Slf4jpublic class Dog implements InitializingBean { private String name = "旺財"; private String color = "黑色"; public Dog() { log.info("---dog的無參構造方法被執行"); } @Override public void afterPropertiesSet() throws Exception { log.info("---afterPropertiesSet被執行"); } public void init() { log.info("---initMethod被執行"); }}
把Dog類注冊到Spring容器中,并設置了Bean實例化后的初始化方法;
@Configurationpublic class SpringConfig { @Bean(initMethod = "init") public Dog dog(){ Dog dog = new Dog(); return dog; }}
2、定義MyBeanPostProcessor,并且實現BeanPostProcessor接口;(這里類的命名和方法內邏輯僅是為了演示需要,實際開發中需要以實際邏輯來替換掉演示內容)
@Component@Slf4jpublic class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("dog")) { log.info("postProcessBeforeInitialization---" + beanName); //如果特定的bean實例化完成后,還未執行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在這里實現 } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("dog")) { log.info("postProcessAfterInitialization---" + beanName); //如果特定的bean實例化完成,InitializingBean.afterPropertiesSet()方法執行后,有一些其他操作,可以在這里實現 } return bean; }}
3、編寫單元測試,來驗證結果;
@SpringBootTest@Slf4jpublic class FanfuApplicationTests { @Test public void test3(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu"); Dog dog = ((Dog) context.getBean("dog")); log.info(dog.getName()); }}
圖片
結論:從單元測試的執行結果來看,驗證了Spring的擴展點BeanPostProcessor的執行時機,即postProcessBeforeInitialization方法的執行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;postProcessAfterInitialization方法的執行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前之后;
以上演示了BeanPostProcessor作為Springboot的擴展點之一的實現方式和執行時機,下面從示例入手,來了解一下其基本的工作原理,正所謂知其然還要知其所以然嘛。
BeanPostProcessor的工作原理的關鍵其實就是兩點,第一,BeanPostProcessor的實現類是什么時候被注冊的?第二,BeanPostProcessor的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法是如何被執行的?
1、BeanPostProcessor中的兩個擴展方法中,postProcessBeforeInitialization方法是先被執行的,即Bean實例化和屬性注入完成之后,通過實現方式示例代碼的Debug,找到了BeanPostProcessor接口的實現類到Spring容器中的入口,即AbstractApplicationContext#refresh--->registerBeanPostProcessors
圖片
2、進入到AbstractApplicationContext#registerBeanPostProcessors方法內,會發現這段代碼很干凈,即依賴于PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法;
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);}
3、進入到PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法又是另一番洞天:第一步,獲取所有實現BeanPostProcessor接口的實現類的名稱,實現方式示例中的MyBeanPostProcessors就在其中;
圖片
第二步,提前注冊BeanPostProcessorChecker,主要用途是用于Bean創建過程中的日志信息打印記錄;
圖片
第三步,就是把所有的BeanPostProcessor接口的實現類,按照是否實現PriorityOrdered接口、是否實現Ordered接口、其他,分為三組;
圖片
最后,下面的內容很長,不過很簡單,即按第二步分成的三類,依次注冊,具體的順序是實現PriorityOrdered接口BeanPostProcessor接口的實現類、實現實現Ordered接口BeanPostProcessor接口的實現類、其他的BeanPostProcessor接口的實現類;
圖片
總結,BeanPostProcessor的注冊時機是在Spring容器啟動過程中,即BeanFactoryPostProcessor擴展點的邏輯執行完成后,緊接著就開始了BeanPostProcessor的注冊,其具體的注冊邏輯在PostProcessorRegistrationDelegate#registerBeanPostProcessors()。
圖片
從實現方式的示例中驗證得知,BeanPostProcessor接口的實現類的執行時機是在Spring管理的Bean實例化、屬性注入完成后,那么找到Dog類的實例化入口,那么離BeanPostProcessor接口的實現類的執行時機也就不遠了。
1、通過Debug調試,注冊到Spring容器中的Dog類的實例化入口,即org.springframework.context.support.AbstractApplicationContext#refresh--->finishBeanFactoryInitialization();
圖片
2、進入到finishBeanFactoryInitialization(),發現實現方式示例中的Dog類是在DefaultListableBeanFactory#preInstantiateSingletons--->getBean()中實例化完成的。這里大致介紹一下getBean()業務邏輯:當獲取某一個bean時,先查詢緩存確定是否存在,若存在,則直接返回,若不存在,則開始創建Bean,若Bean內依賴了另外一個Bean,則是上述過程的一個遞歸。
圖片
3、從getBean方法進入后,主要過程是AbstractBeanFactory#doGetBean-->AbstractBeanFactory#createBean-->AbstractAutowireCapableBeanFactory#doCreateBean-->AbstractAutowireCapableBeanFactory#createBeanInstance,至此完成了Bean的實例化和屬性注入。到這要打起精神了,要找的BeanPostProcessor接口的實現類的執行時機馬上就到。果然在AbstractAutowireCapableBeanFactory#doCreateBean方法中,Dog類實例化完后,又調用initializeBean()進行bean的初始化操作,而BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執行時機分別是在Bean的初始化方法執行前后觸發,那么這個方法大概率就是BeanPostProcessor接口的實現類的執行時機的入口了。
圖片
4、進入到initializeBean()一看,判斷的果然沒錯,先執行BeanPostProcessor接口實現類的postProcessBeforeInitialization方法,接著如果bean實現了InitializingBean或者自定義了initMethod,就會在這里執行InitializingBean#afterPropertiesSet和initMethod方法,最后會執行執行BeanPostProcessor接口實現類的postProcessAfterInitialization方法;
protected Object initializeBean(String beanName, 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()) { //執行BeanPostProcessor接口實現類的postProcessBeforeInitialization方法 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //如果bean實現了InitializingBean或者自定義了initMethod, //會在這里執行InitializingBean#afterPropertiesSet和initMethod方法 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()) { //執行BeanPostProcessor接口實現類的postProcessAfterInitialization方法 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean;}
5、下面分別再進入到applyBeanPostProcessorsBeforeInitialization()、invokeInitMethods()、applyBeanPostProcessorsAfterInitialization(),看看具體是怎么實現的。先來看applyBeanPostProcessorsBeforeInitialization():如果仔細研究過之前的Springboot擴展點之BeanFactoryPostProcessor 、Springboot擴展點之BeanDefinitionRegistryPostProcessor 、Springboot擴展點之ApplicationContextInitializer這幾篇文章,那么對這個方法的套路就再熟悉不過了:先獲取到所有注冊到Spring容器中BeanPostProcessor接口的實現類,然后再遍歷執行觸發方法,就這么樸實無華。
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result;}
6、再來看一下,AbstractAutowireCapableBeanFactory#invokeInitMethods,邏輯也是很清晰,先判斷是否實現了InitializingBean接口,如果實現了InitializingBean接口,就會觸發執行afterPropertiesSet(),然后判斷有沒有自定義initMethod方法,如果有,則在這里開始執行;
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //判斷是否實現了InitializingBean接口 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } }else { //如果實現了InitializingBean接口,就會重寫afterPropertiesSet(),這里就會觸發執行 ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { //判斷有沒有自定義initMethod方法,如果有,則在這里開始執行; String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } }}
7、最后來看一下applyBeanPostProcessorsAfterInitialization(),前面applyBeanPostProcessorsBeforeInitialization()看懂了,這里就沒有必要分析了,如出一轍,熟悉配方,熟悉的味道。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result;}
至此,Springboot擴展點BeanPostProcessor的工作原理分析完了,歸根結底就是兩點,
第一,在Spring容器初始化的過程中,完成擴展點的注冊;
第二,在Spring中Bean完成實例化和屬性注入后,開始觸發已注冊的擴展點的擴展動作。
內容很長,但是邏輯簡單,希望閱讀到這篇文章的小伙伴能夠有耐心看完,因為我在研究清楚整個過程后,我是感覺獲益良多的,希望你也是。
其實了解了BeanPostProcessor的功能特性、實現方式和工作原理,在遇到類似的業務需求的時候都可以應用這個擴展點,這里舉兩個我想到的應用場景:
在程序中我們可以自定義注解并標到相應的類上,當個類注冊到Spring容器中,并實例化完成后,希望觸發自定義注解對應的一些其他操作的時候,就可以通過BeanPostProcessor來實現。
前面有兩篇文章優雅的Springboot參數校驗(一) 、優雅的Springboot參數校驗(二) 和大家分享了參數校驗具體實現方式,其核心原理正是用到了BeanPostProcessor擴展點,具體的實現類是org.springframework.validation.beanvalidation.BeanValidationPostProcessor
本文鏈接:http://www.www897cc.com/showinfo-26-35885-0.htmlSpringBoot擴展點之BeanPostProcessor
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 微服務,其實它也是有很多坑
下一篇: 12種常見的軟件架構風格,架構師必備