成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringBoot擴展點之BeanPostProcessor

開發(fā) 前端
在程序中我們可以自定義注解并標到相應(yīng)的類上,當個類注冊到Spring容器中,并實例化完成后,希望觸發(fā)自定義注解對應(yīng)的一些其他操作的時候,就可以通過BeanPostProcessor來實現(xiàn)。

前言

Springboot(Spring)的擴展點其實有很多,但是都有一個共同點,都是圍繞著Bean和BeanFactory(容器)展開的,其實這也很好理解,Spring的核心是控制反轉(zhuǎn)、依賴注入、面向切面編程,再拋開所有的枝枝節(jié)節(jié),你發(fā)現(xiàn)了什么?Spring提供了一個容器,來管理Bean,整個生態(tài)好像是都圍繞這個展開。研究源碼意義,一方面是在于技術(shù)本身,另一方面也在于理解接受其中的思想。

沒有目的的亂走總是會迷路,有了目標就不一樣了,所以這篇文章是圍繞以下幾個問題展開的,這也是我想和大家分享的內(nèi)容:(如果你和我的疑問一樣,關(guān)注,收藏+點贊,不迷路哦)

1、BeanPostProcessor接口的功能特性是什么樣的?

2、BeanPostProcessor接口怎么實現(xiàn)擴展?

3、BeanPostProcessor接口的實現(xiàn)類的工作原理是什么?

4、BeanPostProcessor接口的應(yīng)用場景有哪些?

功能特性

1、BeanPostProcessor是Bean級別的擴展接口,在Spring管理的Bean實例化完成后,預(yù)留了兩種擴展點;

2、這兩處擴展的實現(xiàn)方式就是實現(xiàn)BeanPostProcessor接口,并將實現(xiàn)類注冊到Spring容器中;

3、兩種擴展點分別是BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法;

4、postProcessBeforeInitialization方法的執(zhí)行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;

5、postProcessAfterInitialization方法的執(zhí)行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之后;

6、BeanPostProcessor接口的實現(xiàn)類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,在在Spring管理的每個bean初始化后都會執(zhí)行到;

實現(xiàn)方式

1、定義一個實體類Dog,并實現(xiàn)InitializingBean接口,并且實現(xiàn)afterPropertiesSet()。其中afterPropertiesSet()和init()是為了演示BeanPostProcessor接口的實現(xiàn)類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執(zhí)行時機;

@Getter
@Setter
@Slf4j
public class Dog implements InitializingBean {
    private String name = "旺財";
    private String color = "黑色";
    public Dog() {
        log.info("---dog的無參構(gòu)造方法被執(zhí)行");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("---afterPropertiesSet被執(zhí)行");
    }
    public void init() {
        log.info("---initMethod被執(zhí)行");
    }
}

把Dog類注冊到Spring容器中,并設(shè)置了Bean實例化后的初始化方法;

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

2、定義MyBeanPostProcessor,并且實現(xiàn)BeanPostProcessor接口;(這里類的命名和方法內(nèi)邏輯僅是為了演示需要,實際開發(fā)中需要以實際邏輯來替換掉演示內(nèi)容)

@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("dog")) {
            log.info("postProcessBeforeInitialization---" + beanName);
            //如果特定的bean實例化完成后,還未執(zhí)行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在這里實現(xiàn)
        }
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("dog")) {
            log.info("postProcessAfterInitialization---" + beanName);
            //如果特定的bean實例化完成,InitializingBean.afterPropertiesSet()方法執(zhí)行后,有一些其他操作,可以在這里實現(xiàn)
        }
        return bean;
    }
}

3、編寫單元測試,來驗證結(jié)果;

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

圖片圖片

結(jié)論:從單元測試的執(zhí)行結(jié)果來看,驗證了Spring的擴展點BeanPostProcessor的執(zhí)行時機,即postProcessBeforeInitialization方法的執(zhí)行時機是在Spring管理的Bean實例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;postProcessAfterInitialization方法的執(zhí)行時機是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前之后;

以上演示了BeanPostProcessor作為Springboot的擴展點之一的實現(xiàn)方式和執(zhí)行時機,下面從示例入手,來了解一下其基本的工作原理,正所謂知其然還要知其所以然嘛。

工作原理

BeanPostProcessor的工作原理的關(guān)鍵其實就是兩點,第一,BeanPostProcessor的實現(xiàn)類是什么時候被注冊的?第二,BeanPostProcessor的實現(xiàn)類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法是如何被執(zhí)行的?

注冊時機

1、BeanPostProcessor中的兩個擴展方法中,postProcessBeforeInitialization方法是先被執(zhí)行的,即Bean實例化和屬性注入完成之后,通過實現(xiàn)方式示例代碼的Debug,找到了BeanPostProcessor接口的實現(xiàn)類到Spring容器中的入口,即AbstractApplicationContext#refresh--->registerBeanPostProcessors

圖片圖片

2、進入到AbstractApplicationContext#registerBeanPostProcessors方法內(nèi),會發(fā)現(xiàn)這段代碼很干凈,即依賴于PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法;

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

3、進入到PostProcessorRegistrationDelegate類的registerBeanPostProcessors()方法又是另一番洞天:第一步,獲取所有實現(xiàn)BeanPostProcessor接口的實現(xiàn)類的名稱,實現(xiàn)方式示例中的MyBeanPostProcessors就在其中;

圖片圖片

第二步,提前注冊BeanPostProcessorChecker,主要用途是用于Bean創(chuàng)建過程中的日志信息打印記錄;

圖片圖片

第三步,就是把所有的BeanPostProcessor接口的實現(xiàn)類,按照是否實現(xiàn)PriorityOrdered接口、是否實現(xiàn)Ordered接口、其他,分為三組;

圖片圖片

最后,下面的內(nèi)容很長,不過很簡單,即按第二步分成的三類,依次注冊,具體的順序是實現(xiàn)PriorityOrdered接口BeanPostProcessor接口的實現(xiàn)類、實現(xiàn)實現(xiàn)Ordered接口BeanPostProcessor接口的實現(xiàn)類、其他的BeanPostProcessor接口的實現(xiàn)類;

圖片圖片

總結(jié),BeanPostProcessor的注冊時機是在Spring容器啟動過程中,即BeanFactoryPostProcessor擴展點的邏輯執(zhí)行完成后,緊接著就開始了BeanPostProcessor的注冊,其具體的注冊邏輯在PostProcessorRegistrationDelegate#registerBeanPostProcessors()。

圖片圖片

執(zhí)行時機

從實現(xiàn)方式的示例中驗證得知,BeanPostProcessor接口的實現(xiàn)類的執(zhí)行時機是在Spring管理的Bean實例化、屬性注入完成后,那么找到Dog類的實例化入口,那么離BeanPostProcessor接口的實現(xiàn)類的執(zhí)行時機也就不遠了。

1、通過Debug調(diào)試,注冊到Spring容器中的Dog類的實例化入口,即org.springframework.context.support.AbstractApplicationContext#refresh--->finishBeanFactoryInitialization();

圖片圖片

2、進入到finishBeanFactoryInitialization(),發(fā)現(xiàn)實現(xiàn)方式示例中的Dog類是在DefaultListableBeanFactory#preInstantiateSingletons--->getBean()中實例化完成的。這里大致介紹一下getBean()業(yè)務(wù)邏輯:當獲取某一個bean時,先查詢緩存確定是否存在,若存在,則直接返回,若不存在,則開始創(chuàng)建Bean,若Bean內(nèi)依賴了另外一個Bean,則是上述過程的一個遞歸。

圖片圖片

3、從getBean方法進入后,主要過程是AbstractBeanFactory#doGetBean-->AbstractBeanFactory#createBean-->AbstractAutowireCapableBeanFactory#doCreateBean-->AbstractAutowireCapableBeanFactory#createBeanInstance,至此完成了Bean的實例化和屬性注入。到這要打起精神了,要找的BeanPostProcessor接口的實現(xiàn)類的執(zhí)行時機馬上就到。果然在AbstractAutowireCapableBeanFactory#doCreateBean方法中,Dog類實例化完后,又調(diào)用initializeBean()進行bean的初始化操作,而BeanPostProcessor接口的實現(xiàn)類的postProcessBeforeInitialization方法和postProcessAfterInitialization方法的執(zhí)行時機分別是在Bean的初始化方法執(zhí)行前后觸發(fā),那么這個方法大概率就是BeanPostProcessor接口的實現(xiàn)類的執(zhí)行時機的入口了。

圖片圖片

4、進入到initializeBean()一看,判斷的果然沒錯,先執(zhí)行BeanPostProcessor接口實現(xiàn)類的postProcessBeforeInitialization方法,接著如果bean實現(xiàn)了InitializingBean或者自定義了initMethod,就會在這里執(zhí)行InitializingBean#afterPropertiesSet和initMethod方法,最后會執(zhí)行執(zhí)行BeanPostProcessor接口實現(xiàn)類的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()) {
       //執(zhí)行BeanPostProcessor接口實現(xiàn)類的postProcessBeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
       //如果bean實現(xiàn)了InitializingBean或者自定義了initMethod,
       //會在這里執(zhí)行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()) {
       //執(zhí)行BeanPostProcessor接口實現(xiàn)類的postProcessAfterInitialization方法
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

5、下面分別再進入到applyBeanPostProcessorsBeforeInitialization()、invokeInitMethods()、applyBeanPostProcessorsAfterInitialization(),看看具體是怎么實現(xiàn)的。先來看applyBeanPostProcessorsBeforeInitialization():如果仔細研究過之前的Springboot擴展點之BeanFactoryPostProcessor 、Springboot擴展點之BeanDefinitionRegistryPostProcessor 、Springboot擴展點之ApplicationContextInitializer這幾篇文章,那么對這個方法的套路就再熟悉不過了:先獲取到所有注冊到Spring容器中BeanPostProcessor接口的實現(xiàn)類,然后再遍歷執(zhí)行觸發(fā)方法,就這么樸實無華。

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,邏輯也是很清晰,先判斷是否實現(xiàn)了InitializingBean接口,如果實現(xiàn)了InitializingBean接口,就會觸發(fā)執(zhí)行afterPropertiesSet(),然后判斷有沒有自定義initMethod方法,如果有,則在這里開始執(zhí)行;

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
    //判斷是否實現(xiàn)了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 {
          //如果實現(xiàn)了InitializingBean接口,就會重寫afterPropertiesSet(),這里就會觸發(fā)執(zhí)行
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
   if (mbd != null && bean.getClass() != NullBean.class) {
       //判斷有沒有自定義initMethod方法,如果有,則在這里開始執(zhí)行;
      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的工作原理分析完了,歸根結(jié)底就是兩點,

第一,在Spring容器初始化的過程中,完成擴展點的注冊;

第二,在Spring中Bean完成實例化和屬性注入后,開始觸發(fā)已注冊的擴展點的擴展動作。

內(nèi)容很長,但是邏輯簡單,希望閱讀到這篇文章的小伙伴能夠有耐心看完,因為我在研究清楚整個過程后,我是感覺獲益良多的,希望你也是。

應(yīng)用場景

其實了解了BeanPostProcessor的功能特性、實現(xiàn)方式和工作原理,在遇到類似的業(yè)務(wù)需求的時候都可以應(yīng)用這個擴展點,這里舉兩個我想到的應(yīng)用場景:

處理自定義注解

在程序中我們可以自定義注解并標到相應(yīng)的類上,當個類注冊到Spring容器中,并實例化完成后,希望觸發(fā)自定義注解對應(yīng)的一些其他操作的時候,就可以通過BeanPostProcessor來實現(xiàn)。

參數(shù)校驗

前面有兩篇文章優(yōu)雅的Springboot參數(shù)校驗(一) 、優(yōu)雅的Springboot參數(shù)校驗(二) 和大家分享了參數(shù)校驗具體實現(xiàn)方式,其核心原理正是用到了BeanPostProcessor擴展點,具體的實現(xiàn)類是org.springframework.validation.beanvalidation.BeanValidationPostProcessor

責任編輯:武曉燕 來源: 凡夫編程
相關(guān)推薦

2023-11-28 08:01:25

2023-11-27 07:26:42

Springboot容器

2023-09-28 08:49:41

springBean

2021-04-12 06:09:38

Spring擴展點應(yīng)用

2021-12-30 08:17:27

Springboot數(shù)據(jù)訪問DataSourceB

2020-10-15 12:52:46

SpringbootJava編程語言

2009-08-13 18:00:48

Eclipse重構(gòu)功能擴展點

2023-05-04 00:06:40

2009-08-28 13:57:29

virtual ove擴展點

2023-11-24 08:00:00

2022-05-30 09:32:07

Spring容器

2010-09-13 15:06:36

2025-05-08 03:25:00

DubboSPI機制

2017-05-09 10:34:21

Spring BootDubbo Activ擴展

2017-04-28 08:32:40

Spring BootDubbo Activ使用

2024-02-01 08:28:28

2025-04-18 05:50:59

Spring接口Aware

2021-09-08 08:26:06

SpringbootBeanPostPro

2009-12-24 17:10:42

WPF動畫類

2023-12-05 07:48:23

SpringBoot
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 可以免费观看的av片 | 中文字幕在线看 | 国产亚洲网站 | 亚洲国产精品99久久久久久久久 | 亚洲第一av | 国产精品a一区二区三区网址 | 成人免费视频网站在线看 | 国产精品久久久久久久久久久免费看 | 亚洲精品一区二三区不卡 | 国产黄色精品在线观看 | 色欧美综合| 国产精品免费小视频 | 亚洲成人第一页 | 久草新在线 | 99久久亚洲 | 欧美视频福利 | 欧美一区二区免费电影 | 日韩高清中文字幕 | 精品三级在线观看 | 中文字幕日韩专区 | 真人女人一级毛片免费播放 | 久久久久国产一区二区三区 | 91精品国产综合久久久久 | 最新黄色在线观看 | 懂色中文一区二区三区在线视频 | 午夜资源 | 国产成人精品一区二 | 欧美一级片在线看 | 日韩视频中文字幕 | 久久午夜剧场 | 99免费在线观看视频 | 国内精品视频在线 | 精品一区二区三区免费视频 | 欧美视频xxx | 91免费福利在线 | 国产精品不卡一区 | 亚洲一区二区三区在线视频 | 久草99| 亚洲一区二区三区四区av | 一区二区不卡 | 99成人 |