自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot擴(kuò)展點(diǎn)之BeanPostProcessor

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

前言

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

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

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

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

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

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

功能特性

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

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

3、兩種擴(kuò)展點(diǎn)分別是BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法;

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

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

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

實(shí)現(xiàn)方式

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

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

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

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

2、定義MyBeanPostProcessor,并且實(shí)現(xiàn)BeanPostProcessor接口;(這里類(lèi)的命名和方法內(nèi)邏輯僅是為了演示需要,實(shí)際開(kāi)發(fā)中需要以實(shí)際邏輯來(lái)替換掉演示內(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實(shí)例化完成后,還未執(zhí)行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在這里實(shí)現(xiàn)
        }
        return bean;
    }


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

3、編寫(xiě)單元測(cè)試,來(lái)驗(yàn)證結(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é)論:從單元測(cè)試的執(zhí)行結(jié)果來(lái)看,驗(yàn)證了Spring的擴(kuò)展點(diǎn)BeanPostProcessor的執(zhí)行時(shí)機(jī),即postProcessBeforeInitialization方法的執(zhí)行時(shí)機(jī)是在Spring管理的Bean實(shí)例化、屬性注入完成后,InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前;postProcessAfterInitialization方法的執(zhí)行時(shí)機(jī)是在InitializingBean#afterPropertiesSet方法以及自定義的初始化方法之前之后;

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

工作原理

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

注冊(cè)時(shí)機(jī)

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

圖片圖片

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

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

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

圖片圖片

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

圖片圖片

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

圖片圖片

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

圖片圖片

總結(jié),BeanPostProcessor的注冊(cè)時(shí)機(jī)是在Spring容器啟動(dòng)過(guò)程中,即BeanFactoryPostProcessor擴(kuò)展點(diǎn)的邏輯執(zhí)行完成后,緊接著就開(kāi)始了BeanPostProcessor的注冊(cè),其具體的注冊(cè)邏輯在PostProcessorRegistrationDelegate#registerBeanPostProcessors()。

圖片圖片

執(zhí)行時(shí)機(jī)

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

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

圖片圖片

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

圖片圖片

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

圖片圖片

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

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

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、再來(lái)看一下,AbstractAutowireCapableBeanFactory#invokeInitMethods,邏輯也是很清晰,先判斷是否實(shí)現(xiàn)了InitializingBean接口,如果實(shí)現(xiàn)了InitializingBean接口,就會(huì)觸發(fā)執(zhí)行afterPropertiesSet(),然后判斷有沒(méi)有自定義initMethod方法,如果有,則在這里開(kāi)始執(zhí)行;

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
    //判斷是否實(shí)現(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 {
          //如果實(shí)現(xiàn)了InitializingBean接口,就會(huì)重寫(xiě)afterPropertiesSet(),這里就會(huì)觸發(fā)執(zhí)行
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
   if (mbd != null && bean.getClass() != NullBean.class) {
       //判斷有沒(méi)有自定義initMethod方法,如果有,則在這里開(kāi)始執(zhí)行;
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

7、最后來(lái)看一下applyBeanPostProcessorsAfterInitialization(),前面applyBeanPostProcessorsBeforeInitialization()看懂了,這里就沒(méi)有必要分析了,如出一轍,熟悉配方,熟悉的味道。

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擴(kuò)展點(diǎn)BeanPostProcessor的工作原理分析完了,歸根結(jié)底就是兩點(diǎn),

第一,在Spring容器初始化的過(guò)程中,完成擴(kuò)展點(diǎn)的注冊(cè);

第二,在Spring中Bean完成實(shí)例化和屬性注入后,開(kāi)始觸發(fā)已注冊(cè)的擴(kuò)展點(diǎn)的擴(kuò)展動(dòng)作。

內(nèi)容很長(zhǎng),但是邏輯簡(jiǎn)單,希望閱讀到這篇文章的小伙伴能夠有耐心看完,因?yàn)槲以谘芯壳宄麄€(gè)過(guò)程后,我是感覺(jué)獲益良多的,希望你也是。

應(yīng)用場(chǎng)景

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

處理自定義注解

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

參數(shù)校驗(yàn)

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

責(zé)任編輯:武曉燕 來(lái)源: 凡夫編程
相關(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擴(kuò)展點(diǎn)應(yīng)用

2021-12-30 08:17:27

Springboot數(shù)據(jù)訪(fǎng)問(wèn)DataSourceB

2020-10-15 12:52:46

SpringbootJava編程語(yǔ)言

2009-08-13 18:00:48

Eclipse重構(gòu)功能擴(kuò)展點(diǎn)

2023-05-04 00:06:40

2009-08-28 13:57:29

virtual ove擴(kuò)展點(diǎn)

2023-11-24 08:00:00

2010-09-13 15:06:36

2022-05-30 09:32:07

Spring容器

2021-09-08 08:26:06

SpringbootBeanPostPro

2017-05-09 10:34:21

Spring BootDubbo Activ擴(kuò)展

2025-04-18 05:50:59

Spring接口Aware

2017-04-28 08:32:40

Spring BootDubbo Activ使用

2024-02-01 08:28:28

2009-12-24 17:10:42

WPF動(dòng)畫(huà)類(lèi)

2023-12-05 07:48:23

SpringBoot

2012-01-17 10:00:34

點(diǎn)贊
收藏

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