属性配置介绍
- Devtools全局配置
- 测试环境的@TestPropertySource注解
- 测试环境properties属性
- 命令行参数
- 命令行参数
- ServletConfig初始化参数
- ServletContext初始化参数
- JNDI属性
- JAVA系统属性
- 操作系统的环境变量
- RandomValuePropertySource随机值属性
- jar包外的application-{profile}.properties
- jar包内的application-{profile}.properties
- jar包外的application.properties
- jar包内的application.properties
- @PropertySource绑定配置
- 默认属性
SpringBoot主要有上面几种配置方式,从上到下优先级递减,优先级高的会覆盖优先级低的配置。
Environment解析
在run方法中,prepareEnvironment按字面意思就是准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
进入prepareEnvironment();
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
分析getOrCreateEnvironment();
// 获取或创建Environment
private ConfigurableEnvironment getOrCreateEnvironment() {
// 存在则直接返回
if (this.environment != null) {
return this.environment;
}
// 根据webApplicationType创建对应的Environment
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment(); // 标准的Servlet环境,也就是我们说的web环境
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();// 标准环境,非web环境
}
}
我们这里是web环境,所以返回的是StandardServletEnvironment对象。看一下StandardServletEnvironment的uml图。
StandardServletEnvironment继承自StandardEnvironment,而StandardEnvironment又继承了AbstractEnvironment,AbstractEnvironment的无参构造方法中调用了customizePropertySources方法,也就说StandardServletEnvironment在实例化的时候,他的customizePropertySources会被调用,customizePropertySources源代码如下
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
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);
}
customizePropertySources方法往propertySources中添加了两个名字叫servletConfigInitParams、servletContextInitParams的StubPropertySource对象,没更多的操作;而StandardEnvironment的customizePropertySources方法则往propertySources中添加了两个包含java系统属性和操作系统环境变量的两个对象:MapPropertySource和SystemEnvironmentPropertySource。
总结下,getOrCreateEnvironment方法创建并返回了一个环境:StandardServletEnvironment
分析configureEnvironment()
configureEnvironment(environment, applicationArguments.getSourceArgs());
进入configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
//添加属性转换相关的成员
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
//这里添加通过硬编码的属性
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
//添加通过命令行参数设置的属性,解析它并封装进SimpleCommandLinePropertySource对象,同时将此对象放到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));
}
}
}
configureProfiles
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
分析listeners.environmentPrepared(environment);
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
我们可以看有7个监听器对ApplicationEnvironmentPreparedEvent事件感兴趣,下面逐一分析每个监听器。
- ConfigFileApplicationListener:
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
进入onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
1、加载EnvironmentPostProcessor列表,仍然是从META-INF/spring.factories中加载(在SpringApplication实例化的时候已经加载了,这次是从缓存中读取),然后实例化;
2、将自己也加入EnvironmentPostProcessor列表;ConfigFileApplicationListener实现了EnvironmentPostProcessor接口,可以看它的类图。
3、对EnvironmentPostProcessor列表进行排序;排序之后,EnvironmentPostProcessor列表图如下:
4、遍历EnvironmentPostProcessor列表,调用每个EnvironmentPostProcessor的postProcessEnvironment方法
SystemEnvironmentPropertySourceEnvironmentPostProcessor
将propertySourceList中名为systemEnvironment的SystemEnvironmentPropertySource对象替换成OriginAwareSystemEnvironmentPropertySource对象,source未变,还是SystemEnvironmentPropertySource对象的source;OriginAwareSystemEnvironmentPropertySource是SystemEnvironmentPropertySourceEnvironmentPostProcessor的静态内部类,且继承自SystemEnvironmentPropertySource。具体这么替换出于什么目的,便于原点查找?暂时还未知。
SpringApplicationJsonEnvironmentPostProcessor
spring.application.json(或SPRING_APPLICATION_JSON)是设置在系统属性或系统环境中;
如果spring.application.json(或SPRING_APPLICATION_JSON)有配置,那么给environment的propertySourceList增加JsonPropertySource,并将JsonPropertySource放到名叫systemProperties的PropertySource前;目前没有配置,那么此环境后处理器相当于什么也没做。
CloudFoundryVcapEnvironmentPostProcessor
云平台是否激活,激活了则给environment的propertySourceList增加名为vcap的PropertiesPropertySource对象,并将此对象放到命令行参数PropertySource(名叫commandLineArgs)后。很显然,我们没有激活云平台,那么此环境后处理器相当于什么也没做。
ConfigFileApplicationListener
添加名叫random的RandomValuePropertySource到名叫systemEnvironment的PropertySource后;
并初始化Profiles;初始化PropertiesPropertySourceLoader和YamlPropertySourceLoader这两个加载器从file:./config/,file:./,classpath:/config/,classpath:/路径下加载配置文件,PropertiesPropertySourceLoader加载配置文件application.xml和application.properties,YamlPropertySourceLoader加载配置文件application.yml和application.yaml。目前我们之后classpath:/路径下有个application.yml配置文件,将其属性配置封装进了一个名叫applicationConfig:[classpath:/application.yml]的OriginTrackedMapPropertySource中,并将此对象放到了propertySourceList的最后。
(转载这位大神的)
其他lisenter作用不大就不逐一分析了
分析bindToSpringApplication(environment);
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
这个方法主要是从environment中获取所有以spring.main开头的属性,绑定到这个SpringApplication对应的属性当中。例如下面这些:
ConfigurationPropertySources.attach(environment);
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
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)));
}
}
将sources封装成了一个名叫configurationProperties的ConfigurationPropertySourcesPropertySource对象,并把这个对象放到了sources的第一个位置。SpringConfigurationPropertySources是一个将MutablePropertySources转换成ConfigurationPropertySources的适配器。
spring profile介绍
<p>A <em>profile</em> is a named, logical group of bean definitions to be registered
* with the container only if the given profile is <em>active</em>. Beans may be assigned
* to a profile whether defined in XML or via annotations; see the spring-beans 3.1 schema
* or the {@link org.springframework.context.annotation.Profile @Profile} annotation for
* syntax details. The role of the {@code Environment} object with relation to profiles is
* in determining which profiles (if any) are currently {@linkplain #getActiveProfiles
* active}, and which profiles (if any) should be {@linkplain #getDefaultProfiles active
* by default}.
Spring中的Profile功能其实早在Spring 3.1的版本就已经出来,它可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。举个更具体的例子,我们以前所定义的Bean,当Spring容器一启动的时候,就会一股脑的全部加载这些信息完成对Bean的创建;而使用了Profile之后,它会将Bean的定义进行更细粒度的划分,将这些定义的Bean划分为几个不同的组,当Spring容器加载配置信息的时候,首先查找激活的Profile,然后只会去加载被激活的组中所定义的Bean信息,而不被激活的Profile中所定义的Bean定义信息是不会加载用于创建Bean的。
上面的解析转载简书
激活profile
可以通过spring.profiles.active=**来设置,需要注意的是spring.profiles.active跟spring.profiles.defual互斥,如果需要配置多个profiles,可以通过spring.profiles.include= X, X 来设置。profile的原理暂时不解析了。
这篇博客主要参考:参考