目录
前言
关于自动配置原理的文章很多,但一般都是独立的讨论,并没有结合Spring Boot启动流程来讲,现在带着两个疑问看源码(包含@ComponentScan注解和@Import注解的处理等):
- Spring Boot源码在哪里读取自动配置的类的?
- Spring Boot源码又在哪里把自动配置的类注入到IOC容器的?
源码入口
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<springbootexceptionreporter> exceptionReporters = new ArrayList<>();
//设置系统属性『java.awt.headless』,为true则启用headless模式支持
configureHeadlessProperty();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
//之后逐个调用其started()方法,广播SpringBoot要开始执行了
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布应用开始启动事件
listeners.starting();
try {
//初始化参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文
refreshContext(context);
//再一次刷新上下文,其实是空方法,可能是为了后续扩展。
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布应用已经启动的事件
listeners.started(context);
//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//应用已经启动完成的监听事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringBoot的启动流程入下图所示,它主要分为加载主启动类和解析启动类两个部分,我将从这两个部分分别开始介绍。
加载主启动类
首先点入SpringApplication.run方法
之后进入SpringApplication.prepareContext方法
之后进入SpringApplication.applyInitializers方法
再返回SpringApplication.prepareContext方法进入load(context, sources.toArray(new Object[0]));方法。
之后进入BeanDefinitionLoader.load方法。
再进入AnnotatedBeanDefinitionReader.register方法并经过AnnotatedBeanDefinitionReader.doRegisterBean最后进入BeanDefinitionReaderUtils.registerBeanDefinition方法,将解析的BeanDefinition注册进入容器。
解析启动类
通过AbstractApplicationContext.refresh方法进入AbstractApplicationContext.invokeBeanFactoryPostProcessors方法
之后进入PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法。
之后进入SharedMetadataReaderFactoryContextInitializer.postProcessBeanDefinitionRegistry方法,这里将自动装配的类放到了容器当中。
再回到PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法,最后转到ConfigurationClassPostProcessor.postProcessBeanFactory方法(注意这里的再回到我在spring2.4.4的源码里是在只是同一方法的后续代码调了这个方法,这里应该也是这个意思,不同版本总体逻辑差不多,细节处有差别,ConfigurationClassPostProcessor类很重要,是java自动配置的核心所在)。
之后进入 ConfigurationClassPostProcessor.processConfigBeanDefinitions方法。
之后进入ConfigurationClassParser.parse方法
进入ConfigurationClassParser.processConfigurationClass方法。
之后进入ConfigurationClassParser.doProcessConfigurationClass方法。
先处理 @ComponentScans注解。
再通过ConfigurationClassParser.getImports方法,最后进入ConfigurationClassParser.collectImports进行@Import注解处理。
之后通过sourceClass.getAnnotationAttributes(Import.class.getName(), "value")这段代码获取注解上的信息。
回到ConfigurationClassParser.parse方法,改方法最后一行调用了DeferredImportSelectorHandler.process方法
往下走会调用DeferredImportSelectorHandler.processGroupImports方法。
之后进入 DeferredImportSelectorGrouping.getImports方法。
再通过DeferredImportSelectorGrouping.getImports方法进入AutoConfigurationGroup.process方法,这个方法是自动装配的重点方法。
getAutoConfigurationEntry 方法详细解释一下(@EnableAutoConfiguration 的具体实现类是AutoConfigurationImportSelector,兜了一大圈回到了AutoConfigurationImportSelector#getAutoConfigurationEntry ,整个过程展示了springboot如何一步一步触发自动装配。这通常是我们看源码的一个疑点,某个功能的源码如何实现的我们很容易找到,但是具体在源码里是如何触发的,我们通常没有头绪。
)
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 通过 SpringFactoriesLoader 类提供的方法加载类路径中的 META-INF 目录下的
// spring.factories 文件中针对 EnableAutoConfiguration 的注册配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 对获得的注册配置类集合进行去重处理,防止多个项目引入同样的配置类
configurations = removeDuplicates(configurations);
// 获得注解中被 exclude 或 excludeName 所排除的类的集合
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查被排除类是否可实例化,是否被自动注册配置所使用,不符合条件则抛出异常
checkExcludedClasses(configurations, exclusions);
// 从自动配置类集合中去除被排除的类
configurations.removeAll(exclusions);
// 检查配置类的注解是否符合 spring.factories 文件中 AutoConfigurationImportFilter 指定的注解检查条件
configurations = getConfigurationClassFilter().filter(configurations);
// 将筛选完成的配置类和排查的配置类构建为事件类,并传入监听器。监听器的配置在于 spring.factories 文件中
// 通过 AutoConfigurationImportListener 指定
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
能够发现getCandidateConfigurations 方法中会通过 SpringFactoriesLoader 类来加载类路径中的 META-INF 目录下的 spring.factories 文件中针对 EnableAutoConfiguration 的注册配置类。
到这里所有的自动化配置类都收集了,那是如何组装成BeanDefinition,注册到ioc容器中的呢?我们再回去看org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
这里看出@ComponetScan和@Import 修饰的类和自配配置的类注册到IOC的时机是不一样的,@ComponetScan在启动类上,它管理的就是启动类之下的子目录所有标注了@Component的bean(@Service这些都是,@Service注解上有@Component),之所以要在底下这个方法注册IOC,看源码是因为@Configuration类有关于条件注解的处理逻辑,在这篇@ConditionalOnBean在方法上失效有相关分析。
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass
下面是Spring Boot 自动装配源码 Debug 主线,感兴趣可以自行跟一下源码。
SpringBoot是如何将自动化配置类加入到ioc容器中的?
SpringBoot源码分析:SpringBoot启动源码(一)
SpringBoot源码分析:SpringBoot自动装配(二)
SpringBoot自动配置实现原理及源码解析(2.3.x)
springboot如何把 bean 加载到 beanDefinitionMap
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_55229531/article/details/131047561