SpringBoot源码学习(三) 环境准备
运行Spring应用程序
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置无头属性, 设置该应用程序即使没有检测到显示器也允许启动
configureHeadlessProperty();
// 从`META-INF/spring.factories`文件集中获取SpringApplicationRunListener的子类listener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 开启事件监听,通知监听者们(listener)执行相应操作
listeners.starting();
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置忽略信息
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 创建应用上下文
context = createApplicationContext();
// 从`META-INF/spring.factories`文件集中获取SpringBootExceptionReporter的子类
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文
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);
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;
}
准备环境
通过观察者模式将环境变量,命令行参数,配置文件属性绑定到Application。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建和配置环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将环境链接到configurationProperties属性名下
ConfigurationPropertySources.attach(environment);
// 发布环境准备事件
listeners.environmentPrepared(environment);
// 绑定到Application
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
依赖图谱
创建和配置环境
根据SpringApplication
构造器中推断出应用类型来实例化不同的环境。参考: SpringBoot源码学习(一) SpringApplication构造器
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
// 标准的Servlet环境
return new StandardServletEnvironment();
case REACTIVE:
// WebFlux环境
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
根据依赖关系实例化StandardServletEnvironment
需实例化父类。推断出首先执行org.springframework.core.env.AbstractEnvironment#AbstractEnvironment
。
public AbstractEnvironment() {
// 创建一个新的Environment实例,在构造过程中回调customPropertySources(MutablePropertySources),以允许子类在适当时操纵PropertySource实例。
customizePropertySources(this.propertySources);
}
自定义属性源
org.springframework.web.context.support.StandardServletEnvironment
重写了customizePropertySources(MutablePropertySources)
方法。
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
/**
* 在servletConfigInitParams中存在的属性将优先于在servletContextInitParams中的属性,
* 并且在上述任何一个中找到的属性都优先于在jndiProperties中发现的属性。
* 以上任何属性均优先于StandardEnvironment超类提供的系统属性和环境变量。
* 与Servlet相关的属性源在此阶段作为`StubPropertySource stubs`添加,
* 一旦实际的ServletContext对象被initPropertySources(ServletContext, ServletConfig)完全初始化变得可用。
* servletConfigInitParams
* servletContextInitParams
* jndiProperties 不一定存在
* systemProperties
* systemEnvironment
*/
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
执行完此处代码,源集合中的name
为:
servletConfigInitParams,
servletContextInitParams,
systemProperties,
systemEnvironment
配置环境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 获取数据转换共享实例
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置属性源
configurePropertySources(environment, args);
// 配置Profiles
configureProfiles(environment, args);
}
配置属性源
在此应用程序的环境中添加,删除或重新排序PropertySource。
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
// this.defaultProperties 不为空,将defaultProperties放入sources尾
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 如果命令行参数存在,判断名称为commandLineArgs的source存不存在,存在替换,不存在放入sources首,参数以命令行优先
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
配置Profiles
配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active
属性激活其他配置文件。
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
// 配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active属性激活其他配置文件。
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
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());
}
}
链接数据源
将环境源链接到configurationProperties
属性名下
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
// 获取sources中名称为configurationProperties的sources,存在将并与现有sources不同内存地址,就删除configurationProperties对应的源。不存在直接将此名称添加到队首
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
发布环境准备事件
void environmentPrepared(ConfigurableEnvironment environment) {
/**
* org.springframework.boot.devtools.restart.RestartApplicationListener,
* org.springframework.boot.context.config.ConfigFileApplicationListener,
* org.springframework.boot.context.config.AnsiOutputApplicationListener,
* org.springframework.boot.context.logging.LoggingApplicationListener,
* org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
* org.springframework.boot.autoconfigure.BackgroundPreinitializer,
* org.springframework.boot.context.config.DelegatingApplicationListener,
* org.springframework.boot.context.FileEncodingApplicationListener
*/
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}