日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

SpringBoot擴展點之BeanPostProcessor

來源: 責編: 時間:2023-12-01 17:15:12 240觀看
導讀前言Springboot(Spring)的擴展點其實有很多,但是都有一個共同點,都是圍繞著Bean和BeanFactory(容器)展開的,其實這也很好理解,Spring的核心是控制反轉、依賴注入、面向切面編程,再拋開所有的枝枝節節,你發現了什么?Spring提供了

前言

Springboot(Spring)的擴展點其實有很多,但是都有一個共同點,都是圍繞著Bean和BeanFactory(容器)展開的,其實這也很好理解,Spring的核心是控制反轉、依賴注入、面向切面編程,再拋開所有的枝枝節節,你發現了什么?Spring提供了一個容器,來管理Bean,整個生態好像是都圍繞這個展開。研究源碼意義,一方面是在于技術本身,另一方面也在于理解接受其中的思想。8lA28資訊網——每日最新資訊28at.com

沒有目的的亂走總是會迷路,有了目標就不一樣了,所以這篇文章是圍繞以下幾個問題展開的,這也是我想和大家分享的內容:(如果你和我的疑問一樣,關注,收藏+點贊,不迷路哦)8lA28資訊網——每日最新資訊28at.com

1、BeanPostProcessor接口的功能特性是什么樣的?8lA28資訊網——每日最新資訊28at.com

2、BeanPostProcessor接口怎么實現擴展?8lA28資訊網——每日最新資訊28at.com

3、BeanPostProcessor接口的實現類的工作原理是什么?8lA28資訊網——每日最新資訊28at.com

4、BeanPostProcessor接口的應用場景有哪些?8lA28資訊網——每日最新資訊28at.com

功能特性

1、BeanPostProcessor是Bean級別的擴展接口,在Spring管理的Bean實例化完成后,預留了兩種擴展點;8lA28資訊網——每日最新資訊28at.com

2、這兩處擴展的實現方式就是實現BeanPostProcessor接口,并將實現類注冊到Spring容器中;8lA28資訊網——每日最新資訊28at.com

3、兩種擴展點分別是BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法;8lA28資訊網——每日最新資訊28at.com

4、postProcessBeforeInitialization方法的執行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;8lA28資訊網——每日最新資訊28at.com

5、postProcessAfterInitialization方法的執行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之后;8lA28資訊網——每日最新資訊28at.com

6、BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,在在Spring管理的每個bean初始化后都會執行到;8lA28資訊網——每日最新資訊28at.com

實現方式

1、定義一個實體類Dog,并實現InitializingBean接口,并且實現afterPropertiesSet()。其中afterPropertiesSet()和init()是為了演示BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執行時機;8lA28資訊網——每日最新資訊28at.com

@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實例化后的初始化方法;8lA28資訊網——每日最新資訊28at.com

@Configurationpublic class SpringConfig {    @Bean(initMethod = "init")    public Dog dog(){        Dog dog = new Dog();        return dog;    }}

2、定義MyBeanPostProcessor,并且實現BeanPostProcessor接口;(這里類的命名和方法內邏輯僅是為了演示需要,實際開發中需要以實際邏輯來替換掉演示內容)8lA28資訊網——每日最新資訊28at.com

@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、編寫單元測試,來驗證結果;8lA28資訊網——每日最新資訊28at.com

@SpringBootTest@Slf4jpublic class FanfuApplicationTests {   @Test    public void test3(){        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");        Dog dog = ((Dog) context.getBean("dog"));        log.info(dog.getName());    }}

圖片圖片8lA28資訊網——每日最新資訊28at.com

結論:從單元測試的執行結果來看,驗證了Spring的擴展點BeanPostProcessor的執行時機,即postProcessBeforeInitialization方法的執行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;postProcessAfterInitialization方法的執行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前之后;8lA28資訊網——每日最新資訊28at.com

以上演示了BeanPostProcessor作為Springboot的擴展點之一的實現方式和執行時機,下面從示例入手,來了解一下其基本的工作原理,正所謂知其然還要知其所以然嘛。8lA28資訊網——每日最新資訊28at.com

工作原理

BeanPostProcessor的工作原理的關鍵其實就是兩點,第一,BeanPostProcessor的實現類是什么時候被注冊的?第二,BeanPostProcessor的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法是如何被執行的?8lA28資訊網——每日最新資訊28at.com

注冊時機

1、BeanPostProcessor中的兩個擴展方法中,postProcessBeforeInitialization方法是先被執行的,即Bean實例化和屬性注入完成之后,通過實現方式示例代碼的Debug,找到了BeanPostProcessor接口的實現類到Spring容器中的入口,即AbstractApplicationContext#refresh--->registerBeanPostProcessors8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

2、進入到AbstractApplicationContext#registerBeanPostProcessors方法內,會發現這段代碼很干凈,即依賴于PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法;8lA28資訊網——每日最新資訊28at.com

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);}

3、進入到PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法又是另一番洞天:第一步,獲取所有實現BeanPostProcessor接口的實現類的名稱,實現方式示例中的MyBeanPostProcessors就在其中;8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

第二步,提前注冊BeanPostProcessorChecker,主要用途是用于Bean創建過程中的日志信息打印記錄;8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

第三步,就是把所有的BeanPostProcessor接口的實現類,按照是否實現PriorityOrdered接口、是否實現Ordered接口、其他,分為三組;8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

最后,下面的內容很長,不過很簡單,即按第二步分成的三類,依次注冊,具體的順序是實現PriorityOrdered接口BeanPostProcessor接口的實現類、實現實現Ordered接口BeanPostProcessor接口的實現類、其他的BeanPostProcessor接口的實現類;8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

總結,BeanPostProcessor的注冊時機是在Spring容器啟動過程中,即BeanFactoryPostProcessor擴展點的邏輯執行完成后,緊接著就開始了BeanPostProcessor的注冊,其具體的注冊邏輯在PostProcessorRegistrationDelegate#registerBeanPostProcessors()。8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

執行時機

從實現方式的示例中驗證得知,BeanPostProcessor接口的實現類的執行時機是在Spring管理的Bean實例化、屬性注入完成后,那么找到Dog類的實例化入口,那么離BeanPostProcessor接口的實現類的執行時機也就不遠了。8lA28資訊網——每日最新資訊28at.com

1、通過Debug調試,注冊到Spring容器中的Dog類的實例化入口,即org.springframework.context.support.AbstractApplicationContext#refresh--->finishBeanFactoryInitialization();8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

2、進入到finishBeanFactoryInitialization(),發現實現方式示例中的Dog類是在DefaultListableBeanFactory#preInstantiateSingletons--->getBean()中實例化完成的。這里大致介紹一下getBean()業務邏輯:當獲取某一個bean時,先查詢緩存確定是否存在,若存在,則直接返回,若不存在,則開始創建Bean,若Bean內依賴了另外一個Bean,則是上述過程的一個遞歸。8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

3、從getBean方法進入后,主要過程是AbstractBeanFactory#doGetBean-->AbstractBeanFactory#createBean-->AbstractAutowireCapableBeanFactory#doCreateBean-->AbstractAutowireCapableBeanFactory#createBeanInstance,至此完成了Bean的實例化和屬性注入。到這要打起精神了,要找的BeanPostProcessor接口的實現類的執行時機馬上就到。果然在AbstractAutowireCapableBeanFactory#doCreateBean方法中,Dog類實例化完后,又調用initializeBean()進行bean的初始化操作,而BeanPostProcessor接口的實現類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執行時機分別是在Bean的初始化方法執行前后觸發,那么這個方法大概率就是BeanPostProcessor接口的實現類的執行時機的入口了。8lA28資訊網——每日最新資訊28at.com

圖片圖片8lA28資訊網——每日最新資訊28at.com

4、進入到initializeBean()一看,判斷的果然沒錯,先執行BeanPostProcessor接口實現類的postProcessBeforeInitialization方法,接著如果bean實現了InitializingBean或者自定義了initMethod,就會在這里執行InitializingBean#afterPropertiesSet和initMethod方法,最后會執行執行BeanPostProcessor接口實現類的postProcessAfterInitialization方法;8lA28資訊網——每日最新資訊28at.com

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接口的實現類,然后再遍歷執行觸發方法,就這么樸實無華。8lA28資訊網——每日最新資訊28at.com

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方法,如果有,則在這里開始執行;8lA28資訊網——每日最新資訊28at.com

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()看懂了,這里就沒有必要分析了,如出一轍,熟悉配方,熟悉的味道。8lA28資訊網——每日最新資訊28at.com

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的工作原理分析完了,歸根結底就是兩點,8lA28資訊網——每日最新資訊28at.com

第一,在Spring容器初始化的過程中,完成擴展點的注冊;8lA28資訊網——每日最新資訊28at.com

第二,在Spring中Bean完成實例化和屬性注入后,開始觸發已注冊的擴展點的擴展動作。8lA28資訊網——每日最新資訊28at.com

內容很長,但是邏輯簡單,希望閱讀到這篇文章的小伙伴能夠有耐心看完,因為我在研究清楚整個過程后,我是感覺獲益良多的,希望你也是。8lA28資訊網——每日最新資訊28at.com

應用場景

其實了解了BeanPostProcessor的功能特性、實現方式和工作原理,在遇到類似的業務需求的時候都可以應用這個擴展點,這里舉兩個我想到的應用場景:8lA28資訊網——每日最新資訊28at.com

處理自定義注解

在程序中我們可以自定義注解并標到相應的類上,當個類注冊到Spring容器中,并實例化完成后,希望觸發自定義注解對應的一些其他操作的時候,就可以通過BeanPostProcessor來實現。8lA28資訊網——每日最新資訊28at.com

參數校驗

前面有兩篇文章優雅的Springboot參數校驗(一) 、優雅的Springboot參數校驗(二) 和大家分享了參數校驗具體實現方式,其核心原理正是用到了BeanPostProcessor擴展點,具體的實現類是org.springframework.validation.beanvalidation.BeanValidationPostProcessor8lA28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-35885-0.htmlSpringBoot擴展點之BeanPostProcessor

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 微服務,其實它也是有很多坑

下一篇: 12種常見的軟件架構風格,架構師必備

標簽:
  • 熱門焦點
  • 一加Ace2 Pro真機揭曉 鈦空灰配色質感拉滿

    終于,在經過了幾波預熱之后,一加Ace2 Pro的外觀真機圖在網上出現了。還是博主數碼閑聊站曝光的,這次的外觀設計還是延續了一加11的方案,只是細節上有了調整,例如新加入了鈦空灰
  • 紅魔電競平板評測:大屏幕硬實力

    前言:三年的疫情因為要上網課的原因激活了平板市場,如今網課的時代已經過去,大家的生活都恢復到了正軌,這也就意味著,真正考驗平板電腦生存的環境來了。也就是面對著這種殘酷的
  • 分享六款相見恨晚的PPT模版網站, 祝你做出精美的PPT!

    1、OfficePLUSOfficePLUS網站旨在為全球Office用戶提供豐富的高品質原創PPT模板、實用文檔、數據圖表及個性化定制服務。優點:OfficePLUS是微軟官方網站,囊括PPT模板、Word模
  • 騰訊VS網易,最卷游戲暑期檔,誰能笑到最后?

    作者:無銹缽來源:財經無忌7月16日晚,上海1862時尚藝術中心。伴隨著幻象的精準命中,碩大的熒幕之上,比分被定格在了14:12,被寄予厚望的EDG戰隊以絕對的優勢戰勝了BLG戰隊,拿下了總決
  • 阿里大調整

    來源:產品劉有媒體報道稱,近期淘寶天貓集團啟動了近年來最大的人力制度改革,涉及員工績效、層級體系等多個核心事項,目前已形成一個初步的&ldquo;征求意見版&rdquo;:1、取消P序列
  • 年輕人的“職場羞恥感”,無處不在

    作者:馮曉亭 陶 淘 李 欣 張 琳 馬舒葉來源:燃次元&ldquo;人在職場,應該選擇什么樣的著裝?&rdquo;近日,在網絡上,一個與著裝相關的帖子引發關注,在該帖子里,一位在高級寫字樓亞洲金
  • 到手價3099元起!iQOO Neo8 Pro今日首銷:安卓性能最強旗艦

    5月23日,iQOO如期舉行了新品發布會,全新的iQOO Neo8系列也正式與大家見面,包含iQOO Neo8和iQOO Neo8 Pro兩個版本,其中標準版搭載高通驍龍8+,而Pro版更
  • 2021中國國際消費電子博覽會與青島國際軟件融合創新博覽會新聞發布會隆重舉行

    9月18日,2021中國國際消費電子博覽會與青島國際軟件融合創新博覽會新聞發布會在青島國際新聞中心隆重舉行。發布會上青島市政府領導聯袂出席,對本次雙展會情
  • 北京:科技教育體驗基地開始登記

      北京“科技館之城”科技教育體驗基地登記和認證工作日前啟動。首批北京科技教育體驗基地擬于2023年全國科普日期間掛牌,后續還將開展常態化登記。  北京科技教育體驗基
Top 主站蜘蛛池模板: 巩留县| 昌宁县| 乌兰浩特市| 梅河口市| 绵阳市| 资阳市| 海城市| 出国| 长汀县| 长泰县| 定陶县| 仁寿县| 习水县| 宜春市| 武清区| 东兰县| 清苑县| 县级市| 浙江省| 博乐市| 常德市| 宁夏| 治县。| 南通市| 桐梓县| 临澧县| 胶南市| 大新县| 连平县| 密云县| 灵寿县| 宝坻区| 全州县| 石屏县| 彭山县| 农安县| 交城县| 柏乡县| 城口县| 法库县| 禹城市|