SpringBoot源码分析:prepareEnviroment()

prepareEnviroment做了什么?

主要作用按顺序加载命令行参数, 系统参数和外部配置文件, 创建并配置Web环境, 获取profiles.active属性, 并发布ApplicationEnvironmentPreparedEvent事件, 之后获取属性时, 按顺序获取, 获取到就立即返回, 实现了属性之间的合理加载与替换

java 复制代码
// 准备环境
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;
}

1.getOrCreateEnvironment()

java 复制代码
// 获取或创建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

相关知识点:

1.SpringBoot配置文件中spring.profiles.active配置详解

我们在开发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参数,动态加载内部配置

相关推荐
Super Rookie2 分钟前
Spring Cloud 企业项目技术选型
后端·spring·spring cloud
程序员小白条6 分钟前
我的第二份实习,学校附近,但是干前端!
java·开发语言·前端·数据结构·算法·职场和发展
钟琛......8 分钟前
java中父类和子类的成员变量可以重名吗
java·开发语言
程序员爱钓鱼4 小时前
限流、控并发、减GC!一文搞懂Go项目资源优化的正确姿势
后端·google·go
一只小青团6 小时前
Python之面向对象和类
java·开发语言
好奇的菜鸟6 小时前
Spring Boot 事务失效问题:同一个 Service 类中方法调用导致事务失效的原因及解决方案
数据库·spring boot·sql
qq_529835357 小时前
ThreadLocal内存泄漏 强引用vs弱引用
java·开发语言·jvm
落笔画忧愁e7 小时前
扣子Coze飞书多维表插件添加数据记录
java·服务器·飞书
来自宇宙的曹先生7 小时前
视频网站弹幕系统简易实现
spring boot·音视频
姑苏洛言9 小时前
编写产品需求文档:黄历日历小程序
前端·javascript·后端