代码
AbstractApplicationContext->refresh():
invokeBeanFactoryPostProcessors(beanFactory);
AbstractApplicationContext->invokeBeanFactoryPostProcessors():
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
/**
* PostProcessor为后置处理器
*/
PostProcessorRegistrationDelegate->invokeBeanFactoryPostProcessors():
// 对beanFactoryPostProcessors分类,如果是BeanDefinitionRegistryPostProcessor的子类,就放到registryProcessors集合中,否则放入regularPostProcessors集合中
......
// 从IoC容器的beanDefinitionNames中找到实现了BeanDefinitionRegistryPostProcessor接口的beanName数组
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// 如果该ppName所对应的类实现了PriorityOrdered接口的话,就加入到currentRegistryProcessors中
// 内部很复杂的实现之后,可以看到org.springframework.context.annotation.internalConfigurationAnnotationProcessor这个ppName所对应的是org.springframework.context.annotation.ConfigurationClassPostProcessor类,它是继承了PriorityOrdered接口的
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 把ConfigurationClassPostProcessor这个类添加到currentRegistryProcessors集合中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 把currentRegistryProcessors里的后置处理器添加到registryProcessors集合中
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // --1
currentRegistryProcessors.clear();
......
这里只讲述第一次找bean的过程,invokeBeanFactoryPostProcessors()
方法完整过程描述见此。
PostProcessorRegistrationDelegate->invokeBeanDefinitionRegistryPostProcessors(): // --1
// 根据不同的后置处理器,执行postProcessBeanDefinitionRegistry方法的结果也不一样
// 以ConfigurationClassPostProcessor举例说明,这里的registry即为IoC容器
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
ConfigurationClassPostProcessor->postProcessBeanDefinitionRegistry():
processConfigBeanDefinitions(registry);
ConfigurationClassPostProcessor->processConfigBeanDefinitions():
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 从beanDef的attributes属性判断,首次进入不可能为true
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
// 如果加了@Configuration,那么对应的attributes为full
// 如果类有@Component,@ComponentScan,@Import,@ImportResource,或方法中有@Bean,则为lite
// 注解的注解也会扫描(以递归的方式实现),直到只剩元注解为止,如例二
// 上面两类注解符合之一的话,就会添加到configCandidates集合中
// 此时只有启动类符合两类注解
} else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
......
parser.parse(candidates); // --2
parser.validate();
// parser.getConfigurationClasses()是以.class为单位的map对象
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
......
/*
configClasses里每个configClass里的@Bean,@Import,@ImportResource加入到IoC容器的beanDefinitionMap和beanDefinitionNames变量中
*/
this.reader.loadBeanDefinitions(configClasses);
......
// 这里的configCandidates仅仅是符合两类注解之一的BeanDefinitionHolder,一般是启动类
ConfigurationClassParser->parse(): // --2
for (BeanDefinitionHolder holder : configCandidates) {
// 解析配置类,扫描文件,符合两类注解之一的封装为ConfigurationClass,添加到configurationClasses对象中
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// 这里,会把默认的配置bean添加到configurationClasses对象中,可以参考我的另一篇文章
this.deferredImportSelectorHandler.process();
ConfigurationClassParser->parse():
processConfigurationClass(new ConfigurationClass(metadata, beanName));
ConfigurationClassParser->processConfigurationClass():
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
// 只有当sourceClass为空时,才结束循环
// 为空的条件就是除了Object类之外,没有父类了
while (sourceClass != null);
ConfigurationClassParser->doProcessConfigurationClass():
// 如果配置类包含了@Component注解(仍然是递归查找),一系列的操作,最终还是调用的doProcessConfigurationClass()方法
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
processMemberClasses(configClass, sourceClass);
}
// 先找出类上的@ComponentScan和@ComponentScans注解的所有属性
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
......
// 如果配置类用到了@ComponentScan注解,扫描包下的.class文件
// 找到包含@Component注解的class,立即添加到IoC容器的beanDefinitionMap和beanDefinitionNames中
// 返回被封装成BeanDefinitionHolder对象的Set集合
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // --3
// 递归,一系列的操作,最终还是调用的doProcessConfigurationClass()方法
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 再一次验证,最终还是会调用doProcessConfigurationClass()方法
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
// 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 处理@ImportResource注解
......
// 处理方法的@bean注解
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 如果有父类的话,最终还是会调用doProcessConfigurationClass()方法
if (sourceClass.getMetadata().hasSuperClass()) {
......
}
ComponentScanAnnotationParser->parse() // --3
// 在此之前找到了basePackages,如果没有显示定义,那么就是启动类下的路径
ClassPathBeanDefinitionScanner->doScan:
/* 查找启动类路径下以.class结尾的文件,返回Resource数组
* 遍历Resource数组,如果属于排除的类,不操作,如果包含@Component或@ManagedBean注解,会添加到candidates集合中
* 详见ClassPathScanningCandidateComponentProvider->scanCandidateComponents
*/
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
总结
如图所示,启动类配置@SpringBootApplication注解,那么此类attributes亦为full总结:
- 两类注解:
- 一类是@Configuration
- 一类是@Component,@ComponentScan,@Import,@ImportResource,或方法中有@Bean
- 资源扫描(Resource数组)
- 有@Component或@ManagedBean注解的
- 从beanDefinitionNames遍历,找出实现了BeanDefinitionRegistryPostProcessor、PriorityOrdered接口的名称,然后实例化,在这里是ConfigurationClassPostProcessor类
- 执行该后置处理器的postProcessBeanDefinitionRegistry()方法,遍历beanDefinitionNames,找到符合两类注解之一的bd,在这里是启动类
- 解析启动类
- 解析配置类:详细
- 将上述两步过程中找到的bean通通放入容器的到bdm和bdn变量中
解析可以理解成一个递归的过程,详细如下:
- 如果带有@Component注解,解析内部类
- 如果带有@PropertySource注解,解析
- 如果带有@ComponentScan或者@ComponentScans注解,那么:扫描路径,找到有@Component或@ManagedBean注解的类,添加到bdm和bdn(只在这一步才会立即添加),设置full或者lite属性,遍历、解析
- 如果带有@Import注解,解析
- 如果被导入的类是普通类,会新生成configClass对象,在其importBy属性体现是谁导入的它,解析
- 如果被导入的类实现了ImportSelector接口
- 如果被导入的类实现DeferredImportSelector接口,说明是延迟导入,发现只有
AutoConfigurationImportSelector
继承了此接口,在解析配置类的时候(上述第四步过程)返回bean全限定名集合,然后新生成configClass对象,继续解析 - 否则,会执行
selectImports(AnnotationMetadata importingClassMetadata)
方法,返回bean全限定名数组,解析全限定名数组所对应的类,然后新生成configClass对象,在其importBy属性体现是谁导入的它(不存在则报错) // --10
- 如果被导入的类实现DeferredImportSelector接口,说明是延迟导入,发现只有
- 如果被导入的类实现了ImportBeanDefinitionRegistrar接口,在原configClass对象的importBeanDefinitionRegistrars属性体现,放入容器时会执行
registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
方法
- 如果@ImportResource,解析
- 如果方法有@Bean注解,添加到configClass对象的beanMethods属性中
- 如果有父类,解析父类
- 每调用一次解析方法,都会被记录在configurationClasses属性中,在this.reader.loadBeanDefinitions(configClasses)方法中最终完成bean的添加
- 这里所指带有@Component注解,只要最终包含即为true,如@Service、@Configuration等诸多注解
--10处举例:
假如a import b,b的selectImports()返回c,该段过程如下:
- a import b,发现b实现了ImportSelector接口,执行selectImports方法返回c
- c是普通类,生成configClass对象,解析c类
- c类解析结束回到b类,b类不是普通类,不会生成configClass对象
- b类解析结束回到a类,a类@Import解析结束,继续进行@ImportResource的解析
条件注解
条件注解指内部含有@Conditional
注解的注解,大部分作用在类和方法。将bean添加到bdm之前,至少会过一遍条件注解,如果不满足条件,就不会注入IoC容器。
- @ConditionalOnClass(T.class) : 只有当前项目存在T类,才会注入
- @ConditionalOnBean(value = T.class, name = "str") : 只有执行到此处的IoC容器中存在T类,有bean名称叫str的,才会注入
- @ConditionalOnMissingBean(value = T.class, name = "str") : 执行到此处的IoC容器中存在T类,或有bean名称叫str的,就不注入
- @Profile(value = "dev") : 只有当前是环境包含dev,才会注入
附录:beanDefinition种类
AnnotatedGenericBeanDefinition
SpringBoot
启动类中SpringApplication构造方法里的参数,称为主源(primarySources成员变量),属于AnnotatedGenericBeanDefinition
- 通过
spring.factories
的org.springframework.boot.autoconfigure.EnableAutoConfiguration
设定,属于AnnotatedGenericBeanDefinition
- 通过
@Import
指定类或者内部非静态类有注解@Configuration
、@Component
,属于AnnotatedGenericBeanDefinition
- 通过
@ComponentScan
注解或者内部静态类有注解@Configuration
、@Component
,属于ScannedGenericBeanDefinition
IoC
内部的,属于RootBeanDefinition
,如后置处理器- 通过
@Bean
注解,属于ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition
- 通过
@EnableConfigurationProperties
注解,属于GenericBeanDefinition