### @SpringBootApplication 有三个重要注解:
1、 @SpringBootConfiguration
由于@SpringBootConfiguration标注了@Configuration注解,所以标注了此注解的类会被解析为bean注册到ioc容器中
具体流程:在SpringApplication.run().prepareContext()方法中,
调用BeanDefinitionLoader.load()把主类解析成BeanDefinition保存到IOC容器中
2、 @ComponentScan 加载时机在哪?refreshContext的invokeBeanFactoryPostProcessors
具体流程:在SpringApplication.run().refreshContext()方法中,
-> AbstractApplicationContext.refresh()
调用invokeBeanFactoryPostProcessors()
-> PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors()
-> ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
-> ConfigurationClassPostProcessor.processConfigBeanDefinitions()
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
1、String[] candidateNames = registry.getBeanDefinitionNames() 获取ioc容器中的所有bean定义
2、ConfigurationClassUtils.checkConfigurationClassCandidate() 检测是ConfigurationClass注解类不,此时只有主类是
3、configCandidates.isEmpty() 判断是否找到@Configuration标注的类,若没有就返回
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
do {
4、parser.parse(candidates); 传入参数是主启动类
this.reader.loadBeanDefinitions(configClasses);
}
}
-> ConfigurationClassParser.parse(candidates)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) { for循环主要处理@ComponentScan注解
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef &&
abstractBeanDef.hasBeanClass()) {
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
// 执行parse重载方法处理@Component、@PropertySources等注解
this.componentScanParser.parse 处理@ComponentScan
-> ComponentScanAnnotationParser.parse()
ClassUtils.getPackageName(declaringClass) 从主类中获取主类所在的包路径
-> ClassPathBeanDefinitionScanner.doScan(StringUtils.toStringArray(basePackages)) 扫描包路径,获取组件
-> ClassPathScanningCandidateComponentProvider.findCandidateComponents()
-> ClassPathScanningCandidateComponentProvider.scanCandidateComponents() 把主类所在包的所有class文件加载到内存
-> ClassPathScanningCandidateComponentProvider..isCandidateComponent() 循环判断class是否是组件,这里把
@ComponentScan、@Configuration标注的类都算
for (TypeFilter tf : this.excludeFilters) excludeFilters里面有主类、
AutoConfigurationExcludeFilter、TypeExcludeFilter,排除掉
for (TypeFilter tf : this.includeFilters) includeFilters里面有
interface org.springframework.steretope.Component、
interface jakarta.annotation.ManagedBean
此时:候选组件扫描会把主类排除(主类以提前注册了),把@Component、@Configuration等标注的都作为候选
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException( );
}
}
this.deferredImportSelectorHandler.process(); 处理@EnableAutoConfiguration的
@Import(AutoConfigurationImportSelector.class)
}
3、@EnableAutoConfiguration 有两个功能:
第一:@Import(AutoConfigurationImportSelector.class)导入第三方包及其本身包中的spirng spi配置的组件
第二:@AutoConfigurationPackage给第三方一个路径,扫描路径下第三方的bean注册到ioc容器中
3.1 AutoConfigurationImportSelector 导入第三方包及其本身包中的spirng spi配置的组件 加载时机在哪?invokeBeanFactoryPostProcessors
具体流程:
-> ConfigurationClassParser.parse(candidates)
...
this.deferredImportSelectorHandler.process() 执行到最后一行
-> ConfigurationClassParser$DeferredImportSelectorHandler.process()
handler.processGroupImports()
-> ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports()
grouping.getImports().forEach(entry -> { processImports() }); 注意:先执行getImports在执行processImports
-> DeferredImportSelectorGrouping.getImports()
this.group.process()
-> AutoConfigurationGroup.process()
-> AutoConfigurationImportSelector.getAutoConfigurationEntry()
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes) spring spi机制获取第三方自动配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes) 经过排除
configurations = getConfigurationClassFilter().filter(configurations) 过滤后交给processImports()
-> ConfigurationClassParser.processImports()
3.2 @AutoConfigurationPackage 它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。
然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。
具体流程
-> ConfigurationClassPostProcessor.processConfigBeanDefinitions()
this.reader.loadBeanDefinitions(configClasses); 此时ConfigurationClassParser.parse(candidates)已执行了
-> ConfigurationClassBeanDefinitionReader.loadBeanDefinitions() 循环遍历每个ConfigurationClass
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
-> ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass()
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod); 处理类路径及框架的所有类中的@Bean
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
-> ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars() 调用到主类的AutoConfigurationPackages.Registrar
把AutoConfigurationPackages作为bean定义存放到ioc容器,第三方就可以扫描这些路径,如mybatis
-> 在MybatisPlusAutoConfiguration$AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions()
List<String> packages = AutoConfigurationPackages.get(this.beanFactory) 调用自动配置包路径,扫描Mapper