主要作用按顺序加载命令行参数, 系统参数和外部配置文件, 创建并配置Web环境, 获取profiles.active属性, 并发布ApplicationEnvironmentPreparedEvent事件, 之后获取属性时, 按顺序获取, 获取到就立即返回, 实现了属性之间的合理加载与替换
// 准备环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment 创建和配置环境
// 获取或创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境:配置PropertySources和activeProfiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
// listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。还记得这个listeners怎么来的吗?
listeners.environmentPrepared(environment);
// 将环境绑定到SpringApplication
bindToSpringApplication(environment);
// 如果是非web环境,将环境转换成StandardEnvironment
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
// 配置PropertySources对它自己的递归依赖
ConfigurationPropertySources.attach(environment);
return environment;
}
// 获取或创建Environment,很显然我们这里是创建StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
// 存在则直接返回
if (this.environment != null) {
return this.environment;
}
// 根据webApplicationType创建对应的Environment
// webApplicationType的值还记得在哪获取到的吗?不知道的请去看我的springboot源码一
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment(); // 标准的Servlet环境,也就是我们说的web环境
}
return new StandardEnvironment(); // 标准环境,非web环境
}
很显然创建一个StandardServletEnvironment对象返回。它的类图如下:
AbstractEnvironment
定义了子类需要实现的类, 并通过模板方法, 在构造函数中, 调用子类的customizePropertySources()方法, 将环境配置全部放入this.propertySources中, AbstractEnvironment实现了getActiveProfiles和setActiveProfiles方法, 分别用来获取和设备spring.profiles.active属性的配置
StandardEnvironment
StandardEnvironment继承了AbstractEnvironment, customizePropertySources代码执行步骤有:
先添加数据systemProperties(系统属性)到父类的propertySource末尾
再添加systemEnvironment(系统环境变量)维护到父类的propertySource末尾
StandardServletEnvironment
我们获取的环境是一个StandardServletEnvironment实例, 实例化StandardServletEnvironment的步骤有3个步骤
调用抽象父类AbstractEnvironment的构造函数
调用当前类的customizePropertySources方法
propertySources列表末尾添加一个名称为servletConfigInitParams的空配置
propertySources列表末尾再添加一个名称为servletContextInitParams的空配置
如果jndi可用, propertySources列表末尾末尾在添加一个名称为jndiProperties的空配置, 由于我们没有使用jndi, 所以不会添加该配置
调用父类StandardEnvironment的customizePropertySources方法
propertySources末尾添加systemProperties(系统属性)
propertySources末尾获取systemEnvironment(系统环境变量)
所以当前StandardServletEnvironment对象的propertySources, 按顺序排列为servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment
prepareEnvironment的详情可参考:
https://www.jianshu.com/p/e26d377cd96f?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
https://www.cnblogs.com/youzhibing/p/9622441.html
??我们在开发Spring Boot应用时,通常同一套程序会被应用和安装到几个不同的环境,比如:开发、测试、生产等。其中每个环境的数据库地址、服务器端口等等配置都会不同,如果在为不同环境打包时都要频繁修改配置文件的话,那必将是个非常繁琐且容易发生错误的事。
(1)通过配置文件实现多环境的配置
??对于多环境的配置,各种项目构建工具或是框架的基本思路是一致的,通过配置多份不同环境的配置文件,再通过打包命令指定需要打包的内容之后进行区分打包,Spring Boot也不例外,或者说更加简单。
在Spring Boot中多环境配置文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识,比如:
application.properties:默认配置
application-dev.properties:开发环境
application-test.properties:测试环境
至于哪个具体的配置文件会被加载,需要在application.properties文件中通过spring.profiles.active属性来设置,其值对应{profile}值。
如:spring.profiles.active=test就会加载application-test.properties配置文件内容
下面,以不同环境配置不同的服务端口为例,进行样例实验。
补充:如果是application.yml,application.properties配置文件同时存在,会以application.properties配置文件为准,因为后加载的配置文件中重复的配置项会覆盖先加载的配置项。两者中如果用spring.profiles.active指定其他配置文件,最终重复项以spring.profiles.active指定的配置文件为准。
(2)除此之外也可,通过@Profile注解匹配active参数,动态加载内部配置