使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类

文章目录

  • 1. 前言
  • 2. 先说结论
  • 3. 代码验证

1. 前言

  1. 在之前工作中需要设计SDK(第三方jar包),该SDK中是有bean对象的,从而需要考虑该包下的会成为Bean的类要如何被spring扫描到从而创建,因此写了如下文章:
    1. 设计第三方jar包中有bean对象时,要如何自动加载到被引用的应用中(EnableAutoConfiguration、BeanDefinitionRegistryPostProcessor使用)
  2. 上文中介绍了三种方式:
    1. 使用@ComponentScan指定需要扫描路径
    2. 使用EnableAutoConfiguration自动装配 + @Configuration
    3. 使用EnableAutoConfiguration自动装配 + BeanDefinitionRegistryPostProcessor
  3. 第一种首先被我排除使用,不优雅,第二种,我不希望每次有个类要创建Bean对象,都要在@Configuration的类中加上@Bean的方法,因此选择了第三种。
  4. 但万万没想要,第三种有大坑,比如它无法创建@Configuration内部中@Bean的类。
  5. 案例如下:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第1张图片
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第2张图片

2. 先说结论

  1. 在第三方jar中,若使用了BeanDefinitionRegistryPostProcessor扫描指定包下创建Bean的时候,会把带@Configuration注解的类创建为Bean对象,但其内部的@Bean、@ConditionalOnBean等类似注解都不会失效
  2. 因为BeanDefinitionRegistryPostProcessor扫描指定包,是真的只是看包内的类上是否有@Component注解,有则创建bean对象而已,不会做其他额外事情。
  3. 想要在第三方jar中@Configuration注解的类A中创建@Bean的对象,得使用自动装配该类A,才会生效
  4. 也可以使用@ComponentScan指定需要扫描路径,这个方式也生效。

3. 代码验证

  1. 通常在自己本项目中是如下使用的:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第3张图片

    即只要被自己扫描扫描到的@Configuration可以生效,因此我们可以在启动类上加上,第三方jar包的路径:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第4张图片

  2. 当然这种方式固然可以解决,但不是最优解,毕竟不希望使用SDK的每个项目都要加上@ComponentScan

  3. 下一种解决方式:在SDK中的自动装配加上@Configuration的类
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第5张图片
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第6张图片

  4. 似乎到这里就应该收尾了,上述也验证了在自动装配中加上@Configuration的类就可以创建内部的被@Bean注解的类了,但这里还有个小细节。

  5. 不知道大家发现没有,在上图中,有SDK中的BeanConfig类,以及本项目中的NeiBuConfig类,仔细观察debug后面的对象类型

  6. 我们发现了,在SDK中的BeanConfig类确实是它自己的Bean对象,而在本项目中的NeiBuConfig类,却是代理对象。如下图:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第7张图片

  7. 来来来,我们现在把SDK中使用BeanDefinitionRegistryPostProcessor的类,取消在spring.factories中的配置,如下图:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第8张图片

  8. 在本项目中,重新加载并查看结果:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第9张图片

  9. 发现现在无论是哪里的Config配置,都是代理对象了,首先先明确一个知识点,spring会把被@Configuration注解的类搞成代理对象,创建内部被@Bean的类

  10. 到这里突然悟了, BeanDefinitionRegistryPostProcessor扫描指定包,是真的看包内的类上是否有@Component注解,有则创建bean对象而已

  11. 到这里可能有人有点懵,这里小总结一下

    1. 使用BeanDefinitionRegistryPostProcessor扫描指定包下创建Bean对象的方式中,其功能是看包下的类上是否有@Component注解,有则创建对象,就仅仅而已
    2. 若遇到包下有@Configuration注解的类,因为@Configuration注解里面有@Component注解,所以包下有@Configuration注解的类会被创建为Bean对象。
    3. 而其实在自身项目中有@Configuration注解的类spring实际上是会生成代理对象,从而创建里面被@Bean的bean对象
    4. 根据上述点,所以BeanDefinitionRegistryPostProcessor的方式中有@Configuration注解的类,只是生成bean对象不是代理对象从而不会创建内部的bean对象
    5. 如果使用BeanDefinitionRegistryPostProcessor的方式扫描包中有@Configuration注解的类,再加上自动装配上带上@Configuration注解的类,则实际上容器中会有类A的两个bean对象
  12. 来,再证明一下,如果BeanDefinitionRegistryPostProcessor扫描指定包下有@Configuration注解的类A,以及使用自动装配加上类A,那么理论上来说会有两个bean对象,有个是类A的普通对象,有个是类A的代理对象,如下图:
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第10张图片
    使用BeanDefinitionRegistryPostProcessor扫描指定包下Bean对象时,@Configuration无法创建内部中@Bean的类_第11张图片

你可能感兴趣的:(小知识,java,spring,spring,boot)