准备运行时环境
new SpringApplication() 完成后,下面开始执行run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
下图对 run 方法执行流程做了一个大致的梳理
4. run():启动SpringApplication
先来看前置准备和运行时环境的准备。
public ConfigurableApplicationContext run(String... args) {
// 4.1 创建StopWatch对象,该组件是用来监控启动时间的,仅用于验证性能,不重要
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 4.2 创建空的IOC容器,和一组异常报告器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 4.3 配置与awt相关的信息,设置应用在启动时,即使没有检测到显示器也允许其继续启动
configureHeadlessProperty();
// 4.4 获取SpringApplicationRunListeners,并调用starting方法(回调机制)
SpringApplicationRunListeners listeners = getRunListeners(args);
// 【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
listeners.starting();
try {
// 将main方法的args参数封装到一个对象中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 4.5 准备运行时环境
/**
* Environment: 它是IOC容器的运行环境,它包括Profile和Properties两大部分,
* 它可以由一个到几个激活的Profile共同配置,它的配置可在应用级Bean中获取。
*
* ConfigurableEnvironment: 大多数(如果不是全部)Environment 类型的类都将实现的配置接口。
* 提供用于设置 Profile 和默认配置文件以及操纵基础属性源的工具。
* 允许客户端通过ConfigurablePropertyResolver 根接口设置和验证所需的属性、自定义转换服务以及其他功能。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
}
4.4 getRunListeners:获取SpringApplicationRunListeners
rivate SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
// 调 getSpringFactoriesInstances 方法,取 spring.factories 中所有 SpringApplicationRunListener
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
继续看一下 SpringApplicationRunListener
源码
public interface SpringApplicationRunListener {
/** 首次启动run方法时立即调用,可用于非常早期的初始化。*/
default void starting() {
}
/** 准备好环境(Environment构建完成),但在创建ApplicationContext之前调用。*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/** 在创建和构建ApplicationContext之后,但在加载之前调用。 */
default void contextPrepared(ConfigurableApplicationContext context) {
}
/** ApplicationContext已加载但在刷新之前调用。*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/** ApplicationContext已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners。*/
default void started(ConfigurableApplicationContext context) {
}
/** 在运行方法彻底完成之前立即调用,刷新ApplicationContext并调用所有CommandLineRunners和ApplicationRunner。*/
default void running(ConfigurableApplicationContext context) {
}
/** 当运行应用程序时发生故障时调用。*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
4.5 prepareEnvironment 准备运行时环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 4.5.1 创建运行时环境,默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 4.5.2 配置运行时环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 【回调】SpringApplicationRunListener的environmentPrepared方法(Environment构建完成,但在创建ApplicationContext之前)
listeners.environmentPrepared(environment);
// 4.5.3 环境与应用绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
4.5.1 getOrCreateEnvironment:创建运行时环境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
// 判断当前Web应用类型
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
根据当前的应用运行环境类型,创建不同的 Environment 。默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
IOC容器的运行环境,它包括Profile和Properties两大部分,它可由一个到几个激活的Profile共同配置,它的配置可在应用级Bean中获取。
4.5.2 configureEnvironment:配置运行时环境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
/**
* ConversionService: 默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
* 这个接口的子类 "DefaultConversionService" 实际上就是它在 SpringWebMvc 中做参数类型转换。
*/
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
4.5.3 bindToSpringApplication:环境与应用绑定
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
/**
* Binder: 使用此绑定器的属性源,绑定指定的 可绑定的目标。
* 说白了,也就是把配置内容绑定到指定的属性配置类中(类似于 @ConfigurationProperties)。
*/
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
} catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
小结
-
SpringApplication 应用中可以使用 SpringApplicationRunListener 来监听 SpringBoot 应用的启动过程。
-
在创建IOC容器前,SpringApplication会准备运行时环境 Environment 。
IOC:创建、初始化IOC容器
run:启动SpringApplication
public ConfigurableApplicationContext run(String... args) {
// ...
try {
// ...
// 4.6 如果有配置 spring.beaninfo.ignore,则将该配置设置进系统参数
configureIgnoreBeanInfo(environment);
// 4.7 打印SpringBoot的banner
Banner printedBanner = printBanner(environment);
// 4.8 创建ApplicationContext
context = createApplicationContext();
// 初始化异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 4.9 初始化IOC容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// ...
}
4.6 configureIgnoreBeanInfo:设置系统参数
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
这里有一个配置:spring.beaninfo.ignore,它的作用是控制是否跳过 BeanInfo 类的搜索
4.7 printBanner:打印Banner
Banner 是一个接口并且内置了一个枚举类型,代表 Banner 输出的模式(关闭、控制台打印、日志输出)。
首先它要看你有没有显式的在 application.properties 中配置 spring.banner.location 这个属性,如果有,就加载它,否则加载默认的位置,叫 banner.txt。
这里体现出了 SpringBoot 的设计原则:约定大于配置。
4.8 createApplicationContext:创建IOC容器
// 默认环境 None
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
for (ApplicationContextFactory candidate : SpringFactoriesLoader
.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
ConfigurableApplicationContext context = candidate.create(webApplicationType);
if (context != null) {
return context;
}
}
return new AnnotationConfigApplicationContext();
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
// REACTIVE 环境
static class Factory implements ApplicationContextFactory {
Factory() {
}
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return webApplicationType != WebApplicationType.REACTIVE ? null : new AnnotationConfigReactiveWebServerApplicationContext();
}
}
// SERVLET 环境
static class Factory implements ApplicationContextFactory {
Factory() {
}
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return webApplicationType != WebApplicationType.SERVLET ? null : new AnnotationConfigServletWebServerApplicationContext();
}
}
可以发现都是创建的基于Annotation的 ApplicationContext。
创建对应的 ApplicationContext 时就已经在父类 GenericApplicationContext 中创建好了 IOC 容器
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
- Servlet - StandardServletEnvironment - AnnotationConfigServletWebServerApplicationContext
- Reactive - StandardReactiveWebEnvironment - AnnotationConfigReactiveWebServerApplicationContext
- None - StandardEnvironment - AnnotationConfigApplicationContext
4.9 prepareContext:初始化IOC容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 将创建好的应用环境设置到IOC容器中
context.setEnvironment(environment);
// 4.9.1 IOC容器的后置处理
postProcessApplicationContext(context);
// 4.9.2 执行Initializer
applyInitializers(context);
// 【回调】SpringApplicationRunListeners的contextPrepared方法(在创建和准备ApplicationContext之后,但在加载之前)
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 创建两个组件:在控制台打印Banner的,之前把main方法中参数封装成对象的组件
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// 4.9.3 加载主启动类
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 4.9.4 注册主启动类
load(context, sources.toArray(new Object[0]));
// 【回调】SpringApplicationRunListeners的contextLoaded方法(ApplicationContext已加载但在刷新之前)
listeners.contextLoaded(context);
}
这一步中会:注册BeanName生成器、设置资源加载器和类加载器、设置类型转换器、执行 Initializer 、加载 primarySources 和 sources 、load 注册主启动类