springboot源码之(bean的递归注册)

在prepareContext中,用loader调用load方法,loader是 BeanDefinitionLoader,在BeanDefinitionLoader的构造方法中,
会实例化一个AnnotatedBeanDefinitionReader,在reader的构造方法中,
有一个静态方法registerAnnotationConfigProcessors,里面注册一个ConfigurationClassPostProcessor,
实现了BeanDefinitionRegistryPostProcessor进而实现了BeanFactoryPostProcessor。
而applicationContext在refresh的时候,调用invokeBeanFactoryPostProcessors(beanFactory);
在该方法里,调用了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors这个静态方法,
这里面取出所有的BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,并调用两个接口的方法,但是最终都是调用
processConfigBeanDefinitions方法,先取出当前工厂中的beanDefinitions(一般除了系统自己的一些indicator,就是run方法中传入的object数组的内容,一般就是一个启动类),
并不是所有的这些初始的beanDefinitions都会被解析
判断的逻辑(LOGIC_A)是:
1、是否有@Import,@Component,@ImportSource,@ComponentScan之一
2、再判断里面是否有@Bean方法。
这个逻辑也很好理解,因为如果这五个注解都没有,那么基本上也就不是一个想引入其他bean的configuration类。
进入候选之后,PostProcessor生成parser,解析候选,而接下来所有的五个注解解析出来的beanDefinition,包括从内部类,和父类中一共七个通道,
不用再判断是否含有这五个注解,这是因为解析的逻辑也是寻找这五个注解,无非就是麻烦一点。
@Import的解析逻辑,对于被import进来的每一个类processImports方法:

1、如果实现了ImportSelector接口,调用selectImports方法,返回一个字符串数组,遍历字符串数组(classname),再次执行processImports方法(如果实现了ImportSelector的子接口DeferredImportSelector,那么先添加到一个list中,后续在parse方法的最后处理,逻辑和先处理一样)。

2、如果实现了ImportBeanDefinitionRegistrar接口,放到一个容器中,后续在ConfigurationClassBeanDefinitionReader的loadBeanDefinitions处理,还是调用registerBeanDefinitions方法,往工厂中注册beanDefinition

3、如果以上两个接口都没有实现,就当做@Configuration来处理,所以很多的Autoconfiguration上面注解@Configuration是没有意义的,因为@EnableAutoConfiguration就是一个@Import注解,把所有jar包里面spring.factories文件里面,EnableAutoConfiguration对应的值取出来,返回。

也就是说ImportSelector接口返回的类,递归到最后要么是实现ImportBeanDefinitionRegistrar接口,直接向工厂注册beanDefinition,要么是什么也没实现,当做@Configuration来处理

@ComponentScan注解的解析比较简单,看看包里面所有的类,根据filter(默认的有两个过滤器,过滤@component@Named等三个注解),
如果属性basepackage为空,默认是类当前所在的文件夹(ClassUtils.getPackageName)。加载,解析五个注解。
@PropertySource是解析配置文件,键值对形式
@ImportResource是导入spring的配置文件
@Bean是生成一个factoryBeanMethod

@Configuration这个注解其实作用不大

@EnableConfigurationProperties import了EnableConfigurationPropertiesImportSelector,这是个实现了ImportSelector接口的类,按照上面的逻辑,selectImports方法返回

ConfigurationPropertiesBindingPostProcessorRegistrar------------EA

和ConfigurationPropertiesBeanRegistrar-----------------EB

 两个实现了ImportBeanDefinitionRegistrar接口的类,按照上面import中2的逻辑,都是向工厂注册bean的方法,

先看EA,registerBeanDefinitions是注册了两个类,meta那个先不管好像没多大用,第二个就是

ConfigurationPropertiesBindingPostProcessor这个类,这是个bean后置处理器,在bean对象生成之后,属性赋值之前调用?postProcessBeforeInitialization方法,看bean有没有注解@ConfigurationProperties,有的话,设置值进去。

再看EB,这个功能有点绕了,是EnableConfigurationProperties注解里的value属性里的值(这里面必须是@ConfigurationProperties的类,简称EC),直接把EC,赋值,注册。如果是一般的被@componentScan到的类,

如果不加上@component注解,是不会被注册到工厂,更别提被bean后处理器检测有没有@ConfigurationProperties了。但是如果是在@EnableConfigurationProperties的value中,就可以不用走扫描这一步,这样就可以引入扫描范围之外的类进来,这个上面和@import的3是一样的,一个类想要被工厂管理,要被扫描到,但是第三方jar包的类,可以通过import进来。当然componentScan可以去扫描第三方的jar包,但是总感觉麻烦了很多,单个引入不方便。

所以@EnableConfigurationProperties其实是有两个功能:引入检测@ConfigurationProperties+@Component(或者相当于这个注解的,比如符合LOGIC_A的内部类和import进来的类)的bean后处理器(重复导入不会出错,因为注册前会判断有没有同类)和在value中直接导入@ConfigurationProperties注解的配置类

为什么import导入的类,不管是不是符合LOGIC_A里的条件,类本身都可以被注册到工厂。但是一个符合LOGIC_A的类的父类,内部类,则要看情况?

A: 走parser的processConfigurationClass(ConfigurationClass configClass) 方法时,类是被封装成configClass当作参数传进来的,会被放到configurationClasses这个map中,递归完成之后会取出来keyset,在reader的loadBeandefinition方法中一一注册。import直接走processConfigurationClass,而ConfigurationClass 的父类是在循环中走的doProcessConfigurationClass方法,父类不会被当作参数传入processConfigurationClass方法中,因此不会被注册(如果加上@component被componentscan扫描到就另当别论,因为componentScan在父类的递归解析之前)。而内部类的解析,会在开始的时候判断是不是符合LOGIC_A,符合,走processConfigurationClass,不符合,跳过。所以内部类有四个注解(除了@component)任何之一,以及@component(这个是自然的)都可以被收到工厂中。而父类有前四个之一就不行,只能有@component才可以

转载于:https://www.cnblogs.com/chuliang/p/9816072.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值