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

SpringBoot擴展點之BeanPostProcessor

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

前言

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
@Slf4j
public 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實例化后的初始化方法;

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

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

@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實例化完成后,還未執行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
@Slf4j
public 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

責任編輯:武曉燕 來源: 凡夫編程
相關推薦

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擴展點應用

2021-12-30 08:17:27

Springboot數據訪問DataSourceB

2020-10-15 12:52:46

SpringbootJava編程語言

2009-08-13 18:00:48

Eclipse重構功能擴展點

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使用

2025-04-18 05:50:59

Spring接口Aware

2024-02-01 08:28:28

2021-09-08 08:26:06

SpringbootBeanPostPro

2009-12-24 17:10:42

WPF動畫類

2023-12-05 07:48:23

SpringBoot
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产午夜av片 | 国产在线观看一区 | 日日夜夜天天久久 | 色网站视频 | 成人福利视频网站 | 99精品久久久久久久 | 黑人久久久 | 青青草一区| 奇米影视77 | 国产精品久久久久久久久久久免费看 | 日韩一区二区三区在线观看 | 精品久久久久香蕉网 | 欧美成人激情 | 亚洲一区二区视频 | 欧美专区在线 | 国产第一亚洲 | www.国产 | 久久99精品久久久久 | 免费成人高清在线视频 | 激情欧美日韩一区二区 | 国产精品一区一区 | 成人精品免费 | 少妇特黄a一区二区三区88av | 婷婷丁香在线视频 | 国产精品国产自产拍高清 | 国产精品一区二区福利视频 | 久久久观看| 久久久久久国模大尺度人体 | 午夜影视 | 日本黄色一级视频 | 成人区精品| xnxx 日本免费 | 亚洲欧美网 | 国产区精品视频 | 国产成人精品午夜视频免费 | 91视视频在线观看入口直接观看 | av三级在线观看 | 91pron在线 | 久久久91精品国产一区二区三区 | 国产午夜精品一区二区三区嫩草 | 91在线网站 |