飞道的博客

Springboot扩展点之BeanPostProcessor

346人阅读  评论(0)

前言

        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方法的执行时机;


  
  1. @Getter
  2. @Setter
  3. @Slf 4j
  4. public class Dog implements InitializingBean {
  5. private String name = "旺财";
  6. private String color = "黑色";
  7. public Dog() {
  8. log.info( "---dog的无参构造方法被执行");
  9. }
  10. @ Override
  11. public void afterPropertiesSet() throws Exception {
  12. log.info( "---afterPropertiesSet被执行");
  13. }
  14. public void init() {
  15. log.info( "---initMethod被执行");
  16. }
  17. }

        把Dog类注册到Spring容器中,并设置了Bean实例化后的初始化方法;


  
  1. @ Configuration
  2. public class SpringConfig {
  3. @Bean(initMethod = "init")
  4. public Dog dog(){
  5. Dog dog = new Dog();
  6. return dog;
  7. }
  8. }

2、定义MyBeanPostProcessor,并且实现BeanPostProcessor接口;(这里类的命名和方法内逻辑仅是为了演示需要,实际开发中需要以实际逻辑来替换掉演示内容)


  
  1. @Component
  2. @Slf 4j
  3. public class MyBeanPostProcessor implements BeanPostProcessor {
  4. @ Override
  5. public Object postProcessBeforeInitialization( Object bean, String beanName) throws BeansException {
  6. if (beanName.equals( "dog")) {
  7. log.info( "postProcessBeforeInitialization---" + beanName);
  8. / /如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
  9. }
  10. return bean;
  11. }
  12. @ Override
  13. public Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException {
  14. if (beanName.equals( "dog")) {
  15. log.info( "postProcessAfterInitialization---" + beanName);
  16. / /如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
  17. }
  18. return bean;
  19. }
  20. }

3、编写单元测试,来验证结果;


  
  1. @SpringBootTest
  2. @Slf 4j
  3. public class FanfuApplicationTests {
  4. @ Test
  5. public void test 3(){
  6. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( "com.fanfu");
  7. Dog dog = ((Dog) context.getBean( "dog"));
  8. log.info(dog.getName());
  9. }
  10. }

        结论:从单元测试的执行结果来看,验证了Spring的扩展点BeanPostProcessor的执行时机,即postProcessBeforeInitialization方法的执行时机是在Spring管理的Bean实例化、属性注入完成后,InitializingBean#afterPropertiesSet方法以及自定义的初始化方法之前;postProcessAfterInitialization方法的执行时机是在InitializingBean#afterPropertiesSet方法以及自定义的初始化方法之前之后;


        以上演示了BeanPostProcessor作为Springboot的扩展点之一的实现方式和执行时机,下面从示例入手,来了解一下其基本的工作原理,正所谓知其然还要知其所以然嘛。

工作原理

        BeanPostProcessor的工作原理的关键其实就是两点,第一,BeanPostProcessor的实现类是什么时候被注册的?第二,BeanPostProcessor的实现类的postProcessBeforeInitialization方法和postProcessAfterInitialization方法是如何被执行的?

注册时机

1、BeanPostProcessor中的两个扩展方法中,postProcessBeforeInitialization方法是先被执行的,即Bean实例化和属性注入完成之后,通过实现方式示例代码的Debug,找到了BeanPostProcessor接口的实现类到Spring容器中的入口,即org.springframework.context.support.AbstractApplicationContext#refresh--->registerBeanPostProcessors

2、进入到AbstractApplicationContext#registerBeanPostProcessors方法内,会发现这段代码很干净,即依赖于PostProcessorRegistrationDelegate类的registerBeanPostProcessors()方法;


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

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方法;


  
  1. protected Object initializeBean (String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  2. if (System.getSecurityManager() != null) {
  3. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  4. invokeAwareMethods(beanName, bean);
  5. return null;
  6. }, getAccessControlContext());
  7. } else {
  8. invokeAwareMethods(beanName, bean);
  9. }
  10. Object wrappedBean = bean;
  11. if (mbd == null || !mbd.isSynthetic()) {
  12. //执行BeanPostProcessor接口实现类的postProcessBeforeInitialization方法
  13. wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  14. }
  15. try {
  16. //如果bean实现了InitializingBean或者自定义了initMethod,
  17. //会在这里执行InitializingBean#afterPropertiesSet和initMethod方法
  18. invokeInitMethods(beanName, wrappedBean, mbd);
  19. }
  20. catch (Throwable ex) {
  21. throw new BeanCreationException(
  22. (mbd != null ? mbd.getResourceDescription() : null),
  23. beanName, "Invocation of init method failed", ex);
  24. }
  25. if (mbd == null || !mbd.isSynthetic()) {
  26. //执行BeanPostProcessor接口实现类的postProcessAfterInitialization方法
  27. wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  28. }
  29. return wrappedBean;
  30. }

5、下面分别再进入到applyBeanPostProcessorsBeforeInitialization()、invokeInitMethods()、applyBeanPostProcessorsAfterInitialization(),看看具体是怎么实现的。先来看applyBeanPostProcessorsBeforeInitialization():如果仔细研究过之前的Springboot扩展点之BeanFactoryPostProcessorSpringboot扩展点之BeanDefinitionRegistryPostProcessorSpringboot扩展点之ApplicationContextInitializer这几篇文章,那么对这个方法的套路就再熟悉不过了:先获取到所有注册到Spring容器中BeanPostProcessor接口的实现类,然后再遍历执行触发方法,就这么朴实无华。


  
  1. public Object applyBeanPostProcessorsBeforeInitialization (Object existingBean, String beanName)
  2. throws BeansException {
  3. Object result = existingBean;
  4. for (BeanPostProcessor processor : getBeanPostProcessors()) {
  5. Object current = processor.postProcessBeforeInitialization(result, beanName);
  6. if (current == null) {
  7. return result;
  8. }
  9. result = current;
  10. }
  11. return result;
  12. }

6、再来看一下,AbstractAutowireCapableBeanFactory#invokeInitMethods,逻辑也是很清晰,先判断是否实现了InitializingBean接口,如果实现了InitializingBean接口,就会触发执行afterPropertiesSet(),然后判断有没有自定义initMethod方法,如果有,则在这里开始执行;


  
  1. protected void invokeInitMethods (String beanName, Object bean, @Nullable RootBeanDefinition mbd)
  2. throws Throwable {
  3. //判断是否实现了InitializingBean接口
  4. boolean isInitializingBean = (bean instanceof InitializingBean);
  5. if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod( "afterPropertiesSet"))) {
  6. if (logger.isTraceEnabled()) {
  7. logger.trace( "Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
  8. }
  9. if (System.getSecurityManager() != null) {
  10. try {
  11. AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
  12. ((InitializingBean) bean).afterPropertiesSet();
  13. return null;
  14. }, getAccessControlContext());
  15. }
  16. catch (PrivilegedActionException pae) {
  17. throw pae.getException();
  18. }
  19. } else {
  20. //如果实现了InitializingBean接口,就会重写afterPropertiesSet(),这里就会触发执行
  21. ((InitializingBean) bean).afterPropertiesSet();
  22. }
  23. }
  24. if (mbd != null && bean.getClass() != NullBean.class) {
  25. //判断有没有自定义initMethod方法,如果有,则在这里开始执行;
  26. String initMethodName = mbd.getInitMethodName();
  27. if (StringUtils.hasLength(initMethodName) &&
  28. !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
  29. !mbd.isExternallyManagedInitMethod(initMethodName)) {
  30. invokeCustomInitMethod(beanName, bean, mbd);
  31. }
  32. }
  33. }

7、最后来看一下applyBeanPostProcessorsAfterInitialization(),前面applyBeanPostProcessorsBeforeInitialization()看懂了,这里就没有必要分析了,如出一辙,熟悉配方,熟悉的味道。


  
  1. public Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName)
  2. throws BeansException {
  3. Object result = existingBean;
  4. for (BeanPostProcessor processor : getBeanPostProcessors()) {
  5. Object current = processor.postProcessAfterInitialization(result, beanName);
  6. if (current == null) {
  7. return result;
  8. }
  9. result = current;
  10. }
  11. return result;
  12. }

        至此,Springboot扩展点BeanPostProcessor的工作原理分析完了,归根结底就是两点,第一,在Spring容器初始化的过程中,完成扩展点的注册;第二,在Spring中Bean完成实例化和属性注入后,开始触发已注册的扩展点的扩展动作。内容很长,但是逻辑简单,希望阅读到这篇文章的小伙伴能够有耐心看完,因为我在研究清楚整个过程后,我是感觉获益良多的,希望你也是。


应用场景

        其实了解了BeanPostProcessor的功能特性、实现方式和工作原理,在遇到类似的业务需求的时候都可以应用这个扩展点,这里举两个我想到的应用场景:

处理自定义注解

        在程序中我们可以自定义注解并标到相应的类上,当个类注册到Spring容器中,并实例化完成后,希望触发自定义注解对应的一些其他操作的时候,就可以通过BeanPostProcessor来实现。

参数校验

        前面有两篇文章优雅的Springboot参数校验(一)优雅的Springboot参数校验(二) 和大家分享了参数校验具体实现方式,其核心原理正是用到了BeanPostProcessor扩展点,具体的实现类是org.springframework.validation.beanvalidation.BeanValidationPostProcessor


转载:https://blog.csdn.net/fox9916/article/details/128917992
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场