Spring Boot源码解析——启动配置原理
文章目录
- Spring Boot源码解析——启动配置原理
- 一、@SpringBootConfiguration
- 1.1 注解分析
- 1.1.1 @SpringBootConfiguration
- 1.1.2 @EnableAutoConfiguration——Spring Boot的全局开关
- 1.2 AutoConfigurationImportSelector类
- 1.3 Conditional机制实现——在配置类上加上相应注解
- 二、启动配置流程与原理
- 2.1 创建SpringApplication对象
- 2.2 run()方法
- 参考文档
一、@SpringBootConfiguration
1.1 注解分析
该注解标注在某个类上说明这个类是Spring Boot的主配置类,Spring Boot应该运行这个类的main方法来启动Spring Boot应用。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 重点分析后三个
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
1.1.1 @SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 配置类
@Configuration
public @interface SpringBootConfiguration {
...
}
- 本质就是个配置类,标注在某个类上,表示这是一个Spring Boot的配置类。
- 配置类 ----- 配置文件:配置类也是容器中的一个组件,也即@Component。
1.1.2 @EnableAutoConfiguration——Spring Boot的全局开关
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- 借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助Spring Boot应用将所有符合条件的 @Configuration配置类都加载到当前Spring Boot创建并使用的IoC容器中。
- 将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中,最终会给容器中导入非常多的自动配置类xxxAutoConfiguration。
- Spring Boot在启动时从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,通过Properties加载资源,并将这些值作为自动配置类导入到容器中。
- 返回的全类名通过反射被实例化,就形成了具体的工厂实例,工厂实例通过createBean() 来生成组件具体需要的bean。
1.2 AutoConfigurationImportSelector类
借助于SpringFactoriesLoader,能够把 spring-boot-autoconfigure.jar/META-INF/spring.factories中的每一个xxxAutoConfiguration文件都加载到容器中。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
...
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
...
return configurations;
}
1.3 Conditional机制实现——在配置类上加上相应注解
spring.factories文件里的每一个xxxAutoConfiguration文件一般都会有下面的条件注解:
二、启动配置流程与原理
启动过程中四个重要的事件回调机制:
- 自定义的话还需要配置在META-INF/spring.factories中:
- ApplicationContextInitializer:扩展该接口,并实现initialize方法,它会在准备IOC容器(prepareContext) 阶段运行。
- SpringApplicationRunListener(监听器):扩展该接口,实现所有方法,它们会在相应阶段执行,同时必须实现一个有参构造器。
- 只需要放在IOC容器中:
- ApplicationRunner
- CommandLineRunner
由于callRunners方法从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,所以自定义的ApplicationRunner必须加@Conponent注解。
@SpringBootApplication
public class SpringBoot02Config02Application {
public static void main(String[] args) {
SpringApplication.run(SpringBoot02Config02Application.class, args);
}
}
- run()——两步:
- 先new一个SpringApplication对象实例;
- 再执行其run()方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
2.1 创建SpringApplication对象
- 构造函数
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
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));
// 根据 classpath 里面是否存在 ConfigurableWebApplicationContext 来决定是否应该创建一个为 Web 应用使用的 ApplicationContext 类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 从多个配置类中找到有 main() 方法的主配置类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
2.2 run()方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 声明 IOC 容器 context
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 从类路径下 META-INF/spring.factories 中获取 SpringApplicationRunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 然后回调所有的获取的 SpringApplicationRunListeners 的 starting() 方法
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
// 根据当前环境利用反射创建 IOC 容器(创建 ApplicationContext)
// 同时决定创建的是 web 环境的 IOC 容器还是普通的 IOC 容器,并将创建好的 IOC 容器返回
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备容器,bean 加载
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新 IOC 容器,执行 IOC 容器初始化,如果是 web 应用还会创建嵌入式的 Tomcat
// 实现 spring-boot-starter-* 的自动化配置的关键,包括 spring.factories 中的配置类的加载,bean 的实例化等
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 调用所有 SpringApplicationRunListener 的 started 方法
listeners.started(context);
// callRunners 方法从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调
// ApplicationRunner 先回调,CommandLineRunner 再回调
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 所有的 SpringApplicationRunListeners 回调 running 方法
listeners.running(context);
// 整个 Spring Boot 应用启动完成以后返回启动的 IOC 容器
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
- 准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 环境有则获取,无则创建
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
// 创建环境完成后回调上面的所有 SpringApplicationRunListeners 的 environmentPrepared 方法,表示环境准备完成
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
- 准备IOC容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 准备上下文环境(IOC 容器),将 environment 保存到 IOC 容器中
context.setEnvironment(environment);
// 注册一些组件
this.postProcessApplicationContext(context);
// 回调之前保存的所有的 ApplicationContextInitializer 的 initialize 方法
this.applyInitializers(context);
// 回调所有的 SpringApplicationRunListener 的 contextPrepared()
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// bean 的加载,逻辑可参考 Spring
this.load(context, sources.toArray(new Object[0]));
// prepareContext 运行完成以后回调所有的 SpringApplicationRunListener 的 contextLoaded 方法
listeners.contextLoaded(context);
}