SpringBoot自动配置原理
前言:这篇文章是在看完SpringBoot自动配置原理后记录的,如果有不对的地方烦请多多指正。
SpringBoot应用启动流程
通过调用追踪,我们发现SpringBoot应用启动时,它做了两件事:创建SpringApplication对象、运行run方法,源代码如下:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
我们接着来看,这两步,主要干了些啥。
- 创建SpringApplication对象
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 判断当前应用是不是一个web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从classpath下的meta-info/spring.factories文件中,找到所有配置的ApplicationContextInitializer,然后保存起来
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从classpath下的的meta-info/spring.factories文件中,找到所有配置的ApplicationListener,然后保存起来
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 找到有main方法的主配置类,只有一个
this.mainApplicationClass = this.deduceMainApplicationClass();
}
- 运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 从classpath下的的meta-info/spring.factories文件中,找到所有配置的ApplicationRunListener,然后回调它们的starting方法
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境,主要做了这几件事:创建环境、使用命令行参数配置环境和回调SpringApplicationRunListener#environmentPrepared方法
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
// 创建ioc容器
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备应用上下文,做了如下几件事:
// ① 将环境应用到ioc容器
// ②调用applyInitializers(ConfigurableApplicationContext context)方法,调用之前保存的ApplicationContextInitializer的initialize()方法
// ③调用SpringApplicationRunListener#contextPrepared()方法
// ④调用SpringApplicationRunListener#contextLoaded()方法
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器,初始化ioc容器,在这里边会进行自动配置
this.refreshContext(context);
// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner,执行它们的run方法
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 执行所有的ApplicationContextListener的started()方法
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
SpringBoot自动配置原理
我们都知道SpringBoot的自动配置是通过使用EnableAutoConfiguration注解开启,在这个注解里边它会为我们的Spring应用引入AutoConfigurationImportSelector组件,这个组件实现了DeferredImportSelector接口;在SpringBoot应用启动过程中,会调用ConfigurationClassParser类的parse(Set configCandidates)方法,这个方法里边最终会调用AutoConfigurationImportSelector的process方法来获取所有能够进行自动配置的配置类;我们来看看ConfigurationClassParser#parse(Set configCandidates)方法的源代码:(注意:ConfigurationClassParser类的外部调用入口在parse(Set configCandidates))
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
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());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 执行找到的DeferredImportSelector ----- 重点在这
this.deferredImportSelectorHandler.process();
}
this.deferredImportSelectorHandler.process()方法里边会调用所有找到的DeferredImportSelector的 process(AnnotationMetadata metadata, DeferredImportSelector selector)方法;执行完之后,再处理找到的自动配置类
整个调用链路如下图:
总结
- SpringBoot应用启动流程,分两步:
- 创建SpringApplication对象
- 执行SpringApplication对象的run方法
-
SpringBoot自动配置原理
SpringBoot应用自动配置原理是通过EnableAutoConfiguration注解开启的,这个注解里边为我们导入了一个组件:AutoConfigurationImportSelector,它实现了DefferedImportSelector接口;在SpringBoot应用启动过程中,刷新ioc容器时,会调用ConfigurationClassParser类的parse()方法,这个方法里边的最后一步会执行所有发现的DefferedImportSelector的process()方法,其中就包括了AutoConfigurationImportSelector组件的process方法;在AutoConfigurationImportSector方法的process()方法中,他会调用一个方法:getAutoConfigurationEntry()方法来获取满足条件的要进行自动配置的配置类;在这个getAutoConfigurationEntry()方法里边做了三件事:读取、去重和过滤;其中读取这一步调用了一个getCandidateConfiguration方法,在这个方法里边使用SpringBoot的SpringFactoriesLoader机制来读取classpath路径下的meta-info/spring.factories文件中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的value - 需要进行自动配置的配置类。