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参数,动态加载内部配置

相关推荐
前端极客探险家4 分钟前
Spring Boot + Vue.js 全栈开发:从前后端分离到高效部署,打造你的MVP利器!
vue.js·spring boot·后端
JAVA学习通4 分钟前
【JavaEE进阶】图书管理系统(未完待续)
java·spring·java-ee
人生偌只如初见4 分钟前
SpringAI学习笔记-MCP客户端简单示例
java·spring·ai·client·mcp
码出极致5 分钟前
Redisson 常用方法介绍
后端
KENYCHEN奉孝10 分钟前
Flask3.1打造极简CMS系统
后端·python·flask
码出极致19 分钟前
Redisson 分布式锁的加锁和解锁原理
后端
东阳马生架构28 分钟前
订单初版—4.取消订单链路中的技术问题说明文档
java
带刺的坐椅29 分钟前
Java MCP 鉴权设计与实现指南
java·安全·ai·solon·mcp
vivo互联网技术30 分钟前
vivo Pulsar 万亿级消息处理实践(3)-KoP指标异常修复
java·大数据·服务器·后端·kafka·消息队列·pulsar