likes
comments
collection
share

spring源码之BeanFactoryPostProcessors执行顺序

作者站长头像
站长
· 阅读数 16

BeanFactoryPostProcessor

当spring启动的时候,要执行容器的初始化,而BeanFactoryPostProcessor翻译过来就是bean工厂的后置处理器。简单来说,就是容器初始化完成之后,可以对容器做的一些后置处理。 spring也会有内置的BeanFactoryPostProcessor来对容器进行后置的处理,当然也可以由程序员实现BeanFactoryPostProcessor,或者BeanDefinitionRegistryPostProcessor接口来实现对容器的后置处理。前者是后者的父类,只不过后者支持动态注册beanDefinition,前者原则上不支持。(说是原则上,实际上是可以强制转换类型来进行注册beanDefinition,但不建议这样做,也不能理解spring的作者的意图)

不同方式实现容器的后置处理

我们在上面说过,我们可以实现BeanFactoryPostProcessor/BeanDefinitionRegistryPostProcessor来对spring容器进行后置处理。代码如下:

@Component
public class A implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
       // 自己的业务逻辑
      log.debug("a-p scan parent postProcessBeanFactory");
   }
}
@Component
public class D implements BeanDefinitionRegistryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      log.debug("d-s scan parent postProcessBeanFactory");
   }

   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      log.debug("d-s scan subclass postProcessBeanDefinitionRegistry register E F");
      BeanDefinitionBuilder e = BeanDefinitionBuilder.genericBeanDefinition(E.class);
      BeanDefinitionBuilder f= BeanDefinitionBuilder.genericBeanDefinition(F.class);
      // 注册beanDefinition
      registry.registerBeanDefinition("e",e.getBeanDefinition());
      registry.registerBeanDefinition("f",f.getBeanDefinition());
   }
}

注意,我们是加了@Component注解的,如果没有这个注解的话,那么spring容器将不会回调我们重写的方法。 那么,除了这两种方式,还有其他方式可以实现我们自己的beanFactoryPostProcessor,并交给spring容器处理,然后回调吗? 答案是有的,我们可以不通过@Component注解,而是通过spring给我们的api方式来加入到spring容器中,并在合适的时机回调我们重写的方法。 代码如下:

@Slf4j(topic = "e")
public class B implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      log.debug("b-p api parent postProcessBeanFactory");
   }
}
@Slf4j(topic = "e")
public class C implements BeanDefinitionRegistryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      log.debug("c-s api parent postProcessBeanFactory");
   }

   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      log.debug("c-s api subclass postProcessBeanDefinitionRegistry");
registry.getBeanDefinition("student");
   }
}
@Test
public void defaultContext(){
  AnnotationConfigApplicationContext
        context = new AnnotationConfigApplicationContext();
  context.addBeanFactoryPostProcessor(new B());
  context.addBeanFactoryPostProcessor(new C());
  context.register(ContextConfig.class);
  context.refresh();
}

上面的代码就是我们通过api的方式加入到spring的容器当中的。

执行顺序

我们的问题来了,这几种加入到spring容器的beanFactoryPostProcessor的回调顺序有什么不一样吗?我们可以通过源码来分析下。 我为了方便,把BeanFactoryPostProcessor称为父类,把BeanDefinitionRegistryPostProcessor称为子类。 点开我们的refresh()方法,主要看下面这里的代码:

spring源码之BeanFactoryPostProcessors执行顺序 我们看invokeBeanFactoryPostProcessors(beanFactory)这个方法。简单说下,这个方法完成BeanFactoryPostProcessors的回调,并且完成了beanDefinition的扫描,我们通过打断点就知道了,当执行完这个方法之后,beanDefinitionMap当中就有beanDefinition这些信息了。 我们深入到代码中,可以看到最终的实现交给了一个委派的类,叫做我们深入到代码中,可以看到最终的实现交给了一个委派的类,叫做我们深入到代码中,可以看到最终的实现交给了一个委派的类,叫PostProcessorRegistrationDelegate。在该类的invokeBeanFactoryPostProcessors()方法中,就是所有beanFactoryPostProcessors的执行顺序了。 我们首先可以看到

spring源码之BeanFactoryPostProcessors执行顺序 这里定义了一个set集合,主要存放了执行过了后置处理器,不过这里除了api提供的,这是为了防止重复执行,这里看到后面就知道了。 再来看下面的代码:

spring源码之BeanFactoryPostProcessors执行顺序 这里先执行了传进来的beanFactoryPostProcessors,也就是通过api提供的bean工厂后置处理器。但是要注意,这里是执行了实现了子类的方法,而不是父类的。也就是说如果重写了子类的方法,并通过api提供的方式加入进来的,最先被执行。 而实现了父类的,仅仅是加入到了一个列表中,缓存起来了,后面才会执行到。 我们执行完api提供的子类实现方法之后,来到下面的代码:

spring源码之BeanFactoryPostProcessors执行顺序 可以看出来,我们先定义了一个子类的列表,用来缓存当前的后置处理器,叫做currentRegistryProcessors。 后面我们会在容器中查找到内置的实现了BeanDefinitionRegistryPostProcessor的后置处理器,并且会判断是不是优先执行的。在spring容器中,其实我们已经有符合这个条件的了,这个就是ConfigurationClassPostProcessor。 注意看,我们调用了beanFactory.getBean()方法,一旦执行了这个方法,这个ConfigurationClassPostProcessor的bean就被创建好了。 后面我们执行invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)这行代码的时候,就会回调ConfigurationClassPostProcessor的方法,完成所有beanDefinition的扫描,放入到了beanDefinitionMap当中。 再看下面的代码:

spring源码之BeanFactoryPostProcessors执行顺序 我们还会执行类似的步骤,这是因为上面我们说了,我们完成了beanDefinition的扫描,也就是说,我们完成了上面所有加了@Component注解的类对象的扫描,包括我们自己实现的BeanFactoryPostProcessor以及BeanDefinitionRegistryPostProcessor,所以还会执行同样的步骤,但是这次还是会先执行实现了BeanDefinitionRegistryPostProcessor的方法。 再来看下面的代码:

spring源码之BeanFactoryPostProcessors执行顺序 我们还有第三次执行呢,并且还是死循环。为什么呢? 这是因为我们在第二次执行的时候,有可能又往spring容器中,通过api的方式注入了新的bean工厂后置处理器。同理,死循环也是这个道理,就是为了把所有的bean工厂后置处理器找出来。 一旦跳出了死循环,就说明没有bean工厂的后置处理器注入了,也就是说子类的方法都完成了。该完成子类的父类方法了。 所以在框出来的部分,先调用了子类的父类方法,然后再调用通过api加入的父类的方法。 前面我们说了,父类不能注册beanDefinition的,所以执行完第三次之后,就不会有新的bean工厂的后置处理器加入到spring容器中了。 最后部分的代码如下:

spring源码之BeanFactoryPostProcessors执行顺序 最后就是通过注解方式加入的父类后置处理器了,当然也会有优先级,比如有没有实现PriorityOrdered接口,有没有实现Ordered接口等。 这部分的代码同学们可以好好体会下,这里就不深入讲解了。

总结

我们通过源码可以看到,spring容器执行BeanFactoryPostProcessors的调用顺序是:

spring源码之BeanFactoryPostProcessors执行顺序 我们通过源码可以看出来,通过@Component注解加入的BeanFactoryProcessor的后置处理器,是很靠后的。这个十分重要,因为如果源码不熟悉的同学,使用BeanFactoryProcessor来对spring容器进行一些后置处理的时候,可能会失效或者实例化出来的bean不完整。 比如我们通过注解的方式,加入新的beanDefinitionRegistryPostProcessor,并在这个后置处理器中加入了一个新的bean,这个bean里面又使用@Bean注解加入新的bean。代码如下:

@Slf4j(topic = "e")
public class X {
   public X(){
      log.debug("x bd create normal");
   }

   @Bean
   public Y y(){
      log.debug("x @bean create Y");
      return new Y();
   }
}

我们在自己beanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()把这个X注册到registry当中去,那么这个Y有办法获取到吗? 答案是无法获取到。 这是因为,我们使用@Componenet注解加入的beanDefinitionRegistryPostProcessor会在ConfigurationClassPostProcessor执行完了才会回调。 而ConfigurationClassPostProcessor做的最重要的一件事情就是,扫描所有的符合要求的类,并解析成beanDefinition放入到beanDefinitionMap当中缓存起来,而这个Y后面才加入了,自然就没被扫描到了,所以在spring容器中也就不存在Y这个bean了。 解决的办法很简单,就是使用优先级高于ConfigurationClassPostProcessor这个后置处理器的把它注入,或者使用ImportBeanDefinitionRegistrar这也可以,不过这个是另外的源码部分了。这里就不做太多的解释了,后面阅读到相关源码的时候会做出解释的。 掌握好BeanFactoryPostProcessors的执行顺序,对于我们拓展spring容器有很大的帮助,以后遇到类似的业务问题也可以解决了,或者开发基于spring的一些中间件的时候也有帮助。这就是阅读spring源码的意义所在。

转载自:https://juejin.cn/post/7238200443581677625
评论
请登录