springboot 启动流程及 ConfigurationClassPostProcessor解析
目录
- 说明
- 一、启动类注解分析
- 二、springboot 启动
- 1、启动方法调用流程
- 2、ConfigurationClassPostProcessor添加到IOC
- 3、主启动类加入到IOC
- 三、ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry执行解析
说明
DefaultListableBeanFactory中registerBeanDefinition来注册bean的描述,创建bean时会获取遍历所有bean的描述,根据bean的描述创建对象。下面重点看调用registerBeanDefinition注册bean描述的时机和获取描述创建对象的时机。
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
一、启动类注解分析
@SpringBootApplication是复合注解,分为三个主要的子注解
第一个@SpringBootConfiguration也是一个复合注解子注解是@Configuration
第二个@EnableAutoConfiguration是复合索引,有两个子注解
@AutoConfigurationPackage的子注解@Import(AutoConfigurationPackages.Registrar.class)
另一个是
@Import(AutoConfigurationImportSelector.class)
第三个@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
整体来看@SpringBootApplication复合注解引入了AutoConfigurationPackages.Registrar、AutoConfigurationImportSelector两个组件(具体两个组件是什么作用可以带着疑问去看源码),同时还添加了@Configuration和@ComponentScan注解。
二、springboot 启动
1、启动方法调用流程
SpringApplication.run(RuoYiApplication.class, args)->
new SpringApplication(primarySources).run(args);
咱们重点看的就是上图圈出来的两个方法,一个是创建ApplicationContext,ApplicationContext 是Spring Framework的核心接口,作为IoC(控制反转)容器的核心实现,负责管理应用中所有Bean的创建、依赖注入及生命周期,并提供国际化支持、资源加载、事件发布等企业级功能,我理解就是创建了IOC容器上下文。第二个是刷新ApplicationContext ,进行后置处理器调用和Bean的创建及注入逻辑。下面代码是创建ApplicationContext 的代码,咱们是web服务所以创建了AnnotationConfigServletWebServerApplicationContext。
static class Factory implements ApplicationContextFactory {@Overridepublic ConfigurableApplicationContext create(WebApplicationType webApplicationType) {return (webApplicationType != WebApplicationType.SERVLET) ? null: new AnnotationConfigServletWebServerApplicationContext();}}
下面看一下AnnotationConfigServletWebServerApplicationContext的构造器
public AnnotationConfigServletWebServerApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}
创建了AnnotatedBeanDefinitionReader,继续看AnnotatedBeanDefinitionReader的构造器
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");this.registry = registry;this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}
2、ConfigurationClassPostProcessor添加到IOC
重点分析一下这个 方法AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
会判断容器里面有没有ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
、CommonAnnotationBeanPostProcessor的BeanDefinition,如果没有会添加上相应的BeanDefinition到IOC,看一下registerPostProcessor方法
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(beanName, definition);return new BeanDefinitionHolder(definition, beanName);}
registry.registerBeanDefinition进入AnnotationConfigServletWebServerApplicationContext的父类GenericApplicationContext.registerBeanDefinition,beanFactory是在GenericApplicationContext的无参构造器里面创建DefaultListableBeanFactory类型。
public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();}
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {this.beanFactory.registerBeanDefinition(beanName, beanDefinition);}
继续跟踪到DefaultListableBeanFactory中registerBeanDefinition,把bean的Definition放入到Map集合中,把bean的名字放入到beanDefinitionNames数组。这就到了上面说明里面的内容。
this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);
3、主启动类加入到IOC
查看一下启动方法中的prepareContext,new SpringApplication的时候在构造器里面把把主启动类赋值给primarySources。
getAllSources方法获取了主启动类,执行load方法。
生成BeanDefinitionLoader类,继续调用load方法。
继续调用load方法,当前主启动类时Class的子类,会进入第一个if判断。
继续跟踪到this.annotatedReader.register(source);->
registerBean(componentClass);->
doRegisterBean->
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);->
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
可以看到上面调用栈,最终调用registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),上面分析过会方法到Map集合。
三、ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry执行解析
看一下refreshContext
最后进入AnnotationConfigServletWebServerApplicationContext的父类AbstractApplicationContext中的refresh方法,重点看一下invokeBeanFactoryPostProcessors方法。
重点分析一下这个方法PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
1、获取类型是BeanDefinitionRegistryPostProcessor的bean的名称。
2、根据通过ioc容器创建BeanDefinitionRegistryPostProcessor类型的组件。
3、执行组件中的方法。
通过查看ConfigurationClassPostProcessor的继承关系,ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,所以上面的就是ConfigurationClassPostProcessor的创建过程。
下面看一下invokeBeanDefinitionRegistryPostProcessors方法,遍历所有BeanDefinitionRegistryPostProcessor类型组件,执行postProcessBeanDefinitionRegistry方法,这个集合中就包含ConfigurationClassPostProcessor。
private static void invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process").tag("postProcessor", postProcessor::toString);postProcessor.postProcessBeanDefinitionRegistry(registry);postProcessBeanDefRegistry.end();}
}
继续调用
红框中就是获取了前面放入的BeanDefinitionNames数组,数组中的数据如果添加进去的在上面分析过。
1、根据beanName获取BeanDefinition。2、判断是不是有@Configuration的组件。3、排序集合中BeanDefinition中order大小,order越小的越优先解析。
调用parser.pase方法,开始解析有@Configuration的组件。
遍历集合中的组件开始解析parse->processConfigurationClass->doProcessConfigurationClass
第一个方法是获取内部方法,如果内部方法有@Configure主键,继续递归解析。第二个方法是解析@PropertySource注解。
解析@ComponentScans和@ComponentScan注解,把扫描到的包路径下所有类设置成BeanDefinitonHolder集合如果是有@Configuration的类继续递归,普通的bean会registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
具体扫描包的逻辑在
// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
如果在@ComponentScan中没有配置BasePackages,会使用当前解析的类的包路径为basePackages,这就解释了默认扫描springboot的主启动类包路径下类。
调用到ClassPathBeanDefinitionScanner类中scanner.doScan(StringUtils.toStringArray(basePackages))
又回到说明中的逻辑,往集合中放入类描述。
下面看一下上面留下来的疑问,主启动类中import两个组件,
@Import(AutoConfigurationPackages.Registrar.class)
@Import(AutoConfigurationImportSelector.class)会在解析@Import注解的方法中获取。
获取@import注解中类名,放入到Set集合中。
遍历set集合,判断是不是ImportSelector类型,如果是会使用反射创建组件,执行主键的Aware方法。如果是DeferredImportSelector类型会放入到集合中后面处理。
如果是DeferredImportSelector类型会放入到集合中后面处理,否则进入else调用组件的selectImports方法。
回过头看AutoConfigurationImportSelector是DeferredImportSelector,会暂时在DeferredImportSelectorHandler类中的deferredImportSelectors的list数组中。
继续往下看如果是ImportBeanDefinitionRegistrar,会使用反射创建组件,调用Aware初始化方法,最后存入到了ConfigurationClass中的importBeanDefinitionRegistrars的map集合中,如果既不是ImportSelector也不是ImportBeanDefinitionRegistrar会继续调用processConfigurationClass递归。
回过头看AutoConfigurationPackages.Registrar.class是ImportBeanDefinitionRegistrar类型的。
在回到上一面,这个this.reader.loadBeanDefinitions负责调用刚才import的类,和@Bean注解的方法,进行registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
这个是处理@Bean注解的组件,
如果@Bean里面没有设置beanName会以类名第一个字母小写为beanName.以前很纳闷从哪实现的,现在找到地方了。
AutoConfigurationPackages.Registrar的register方法在这里面执行。
上面提到过这个集合中存入的是AutoConfigurationImportSelector,所以会进入if条件中
执行register获取了AutoConfigurationImportSelector中的AutoConfigurationGroup,往this.groupings这个map中放入key是group对象,value是DeferredImportSelectorGrouping组件。
DeferredImportSelectorGrouping类中放入deferedImport对象
继续跟踪
遍历map集合groupings,grouping.getImports获取DeferredImportSelectorGrouping类中放入deferedImport对象
执行AutoConfigurationImportSelector内部类AutoConfigurationGroup中的process方法,
// AutoConfigurationImportSelector
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {// 1. 判断自动配置是否开启(默认是开启的)// 对应配置项:spring.boot.enableautoconfigurationif (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 2. 获取注解的属性,比如 @EnableAutoConfiguration(exclude = {...})AnnotationAttributes attributes = getAttributes(annotationMetadata);// 3. 【关键步骤】从 META-INF/spring.factories 文件中获取所有候选的配置类// key 为:org.springframework.boot.autoconfigure.EnableAutoConfigurationList<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 4. 去除重复的配置类(防止多个 Jar 包都有 spring.factories 定义)configurations = removeDuplicates(configurations);// 5. 根据 @EnableAutoConfiguration 的 exclude 和 excludeName 属性,排除不需要的配置类Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 6. 【核心过滤】根据条件注解(@ConditionalOnClass, @ConditionalOnMissingBean 等)进行过滤// 这是自动配置“智能”与否的关键!configurations = getConfigurationClassFilter().filter(configurations);// 7. 触发自动配置导入事件,让监听器可以做一些事情fireAutoConfigurationImportEvents(configurations, exclusions);// 8. 返回最终决定要导入的配置类列表return new AutoConfigurationEntry(configurations, exclusions);
DeferredImportSelector: 这是 ImportSelector 的一个子接口。这是最关键的一点。它表示导入选择的执行会被延迟,直到所有 @Configuration 类都被处理完之后。这确保了自动配置可以在用户自定义的 Bean 被注册之后再进行,从而能够根据完整的上下文(如有无用户自定义的 DataSource Bean)做出正确的条件判断。
最终再次进入解析Configuration递归方法。