目的
在前面的自动扫描bean原理这篇文章中,主要说的是spring是如何将@ComponentScan注解声明的包下,加了@Component注解的业务类扫描到spring容器中的;在这篇文章中,没有说明一个知识点,在这里说明一下:
spring将业务类转换为BeanDefinition的方式有三种
- @ComponentScan注解+@Component注解
- @Bean
- @Import注解,引入ImportBeanDefinitionRegistrar和ImportSelector的实现类
这是我目前通过源码学习,总结出来的,当然,也有可能不全,后面学习到了 再来这里补充
本文,就是要说明第二种对业务bean进行转换beanDefinition的源码解析,我不确定自己能不能把这个知识点讲解清楚,试一下吧
源码
这个gif是我们今天要说的源码,前面的调用逻辑,这里其实就是要调用到
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
这个方法的逻辑;该方法是什么时候执行到的,为什么要执行这个方法,我在上篇博客中有写到;这里我们只关心@Bean的处理逻辑:
我们来看上个gif最后调用的方法
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
/**
* 根据beanDefinition的不同来做不同的处理
* 如果bean是配置类(加了@Configuration注解的配置类),那这里就会走下面的第一个分支的判断条件
* 因为在将配置类放到beanDefinitionMap中的时候,是将配置类声明为了AnnotatedGenericBeanDefinition类型的
*/
if (bd instanceof AnnotatedBeanDefinition) {
/**
* 如果将bean存入到beanDefinitionMap第四步
*
* 在这里注入的只是普通的bean,普通的bean 就是指加了@Component注解的bean
* 何为不普通的bean? @Bean 各种beanFactoryPostProcessor获取到的bean不在这里注入 但是是在这里解析 只是不是在这里put到BeanDefinitionMap中的
*/
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
processDeferredImportSelectors();
}
这里会根据beanDefinition的不同类型,来进行不同的处理;由于这里的configCandidates是配置类,所以是AnnotatedBeanDefinition,因为在前面调用AnnotationConfigApplicationContext构造方法的时候,会把配置类注入到BeanDefinitionMap中,创建的是AnnotatedGenericBeanDefinition类型的
在parse()方法中,会执行以下的调用链,中间的一些细节,我们暂时不做解析,我们来看doProcessConfigurationClass()方法
org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Process any @ComponentScan annotations
/**
* 序号1:
* 判断类中是否加了@ComponentScan注解
* 从matedata 里面拿出@ComponentScan注解的属性信息,在后面的方法中,从这个属性里面获取到value,也就是要扫描的包进行注入
*/
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
/**
* 如果将bean存入到beanDefinitionMap第6步
*
* 扫描所有的普通类
* 当这个方法执行完之后,将配置类上@ComponentScan要注入的bean已经注入到map中了
* 但是如果配置类有@Import的话,是在下面processImports()完成的
*
* 这里是获取到当前配置类下的所有的要扫描的包路径信息
*/
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser