Springboot 应用中,@Component 注解是如何生效的 等

1.Spring.factories 里面可以写BeanDefinitionRegistryPostProcessor,也可以写ImportBeanDefinitionRegistrar。实现里面直接new ClassPathBeanDefinitionScanner(registry)
Scan(String… basePackages) 就行了。

为什么这两种都可以写呢?
先来解释为什么可以写BeanDefinitionRegistryPostProcessor。
在解释之前,得先讲一下spring.factories的工作原理,才能解释清楚上面两个问题。
Springboot 中当你新建了一个启动类,这个启动类上面必然有着@SpringbootApplication 注解,这个注解包含了EnableAutoConfiguration 注解,这个EnableAutoConfiguration注解又包含了一个@Import注解。这个@Import注解里面的属性指定了引入AutoConfigurationImportSelector.class 这个类,这个类里面的实现,导入了classpath 下,所有spring.factories 文件中指定的类作为 配置类。

那是什么时候会解析到启动类呢?
首先来看我为什么要问这个问题?因为你光启动类有这个@Import注解,它是起不了作用的,它只是光秃秃的在注解在启动类上。直到当解析到启动类这个配置类时,才会解析到它上面的@Import注解,然后把@Import注解上面的BeanDefinition 注册进BeanFactory。

那我再问一遍,再强调一遍,什么时候会解析到启动类这个配置类呢?

咱们从启动类运行main()方法,main方法在虚拟机之上肯定是可以直接运行的。
启动类的main方法作为入口,运行之后,经过几个SpringApplication 的run()方法,在最后一个run()方法上,有一行代码会createApplicationContext()。这里面createApplicationContext 会调用AnnotationConfigServletWebApplicationContext 的构造方法,这个构造方法会调它的父类的构造方法,它的父类的构造方法又会调它的父类的构造方法,……,其中有一个父类的构造方法,会new AnnotatedBeanDefinitionReader(). AnnotatedBeanDefinitionReader 的构造方法里面,最后一行,会调用一个xxutil类的某个方法,来注册几个BeanDefinition。其中有一个非常重要的BeanDefinition,就是ConfigurationClassPostProcessor。这个ConfigurationClassPostProcessor 是一个BeanDefinitionRegistryPostProcessor。 咱们先记着这个ConfigurationClassPostProcessor ,它在后面invokeBeanFactoryPostProcessor时会调用到。

在invokeBeanFactoryPostProcessor 之前,有一个方法,prepareContext() 会被调用,prepareContext 方法中,会把启动类的BeanDefinition注册进BeanFactory(运行启动类时,main方法传入的参数哪个class,就会注册哪个class,一般传入的参数都是启动类本身的class)。

prepareContext()调用完了之后,后续会调context.refresh().refresh里面,其中有一步就是invokeBeanFactoryPostProcessor(),咱们在上面说完注册ConfigurationClassPostProcessor这个BeanDefinition 之后,也顺便提了一句说ConfigurationClassPostProcessor 会在invokeBeanFactoryPostProcessor()的时候被调用到。它为什么会被调用到呢?InvokeBeanFactoryPostProcessor 里面,会有一步遍历BeanFactory中所有的BeanDefinitionRegistryPostProcessor ,在循环体里面分别调用他们的实现方法——postProcessBeanDefinitionRegistry方法。而ConfigurationClassPostProcessor 正好是其中的一个BeanDefinitionRegistryPostProcessor (这个前面说过,已经把它注册进BeanFactory,所以这里能遍历到,并且执行。)

执行ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry方法时,会遍历所有已注册的BeanDefinition,这里就会遍历到启动类这个BeanDefinition, 它是在prepareContext 的时候被注册进来的,前面已经讲过。

遍历时,会判断每一个BeanDefinition 中的类是否是一个配置类,显然启动类是一个配置类,因为@SpringbootApplication这个注解中包含着@Configuration。

然后如果是配置类,下面就会遍历这些配置类,解析这些配置类,解析出更多的BeanDefinition。

其中有一步,就是处理配置类中的@Import注解。那么一开始提到的spring.factories的工作原理此时就发挥作用了。

当到加载某一个Spring.factories 指定的Bean时,如果它是ImportBeanDefinitionRegistrar,那么就会把它放到它的配置类的一个Map中,在加载它的配置类的BeanDefinition的末尾,会调他的registerBeanDefinitions方法。

如果它是BeanDefinitionRegistryPostProcessor。在前面说的invokeBeanFactoryPostProcessor中,在调完之前已经注册的BeanDefinitionRegistryPostProcessor 之后,可能有一些BeanDefinitionRegistryPostProcessor 会在调之前BeanDefinitionRegistryPostProcessor 注册进来,所以后面有一步还会再调一次BeanFactory内部所有未调过的BeanDefinitionRegistryPostProcessor,此时spring.factories指定的BeanDefinitionRegistryPostProcessor 就会被调用。而且是一直迭代不断地调用,直到没有BeanDefinitionRegistryPostProcessor再注册进来。

这样就解释清楚了:Spring.factories 里面可以写BeanDefinitionRegistryPostProcessor,也可以写ImportBeanDefinitionRegistrar。

顺便再解释一下,@Component 这些注解所注解的类,是什么时候被转成相应的BeanDefinition后注册进来的,就是在 ConfigurationClassPostProcessor 这个BeanDefinitionRegistryPostProcessor 执行的时候。

为什么默认只扫启动类所在的包下?也是在ConfigurationClassPostProcessor 这个BeanDefinitionRegistryPostProcessor 执行的时候,在处理启动类的@ConponentScan 这个注解的时候,如果没有指定,那么它就默认取主动类所在的包来扫描。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值