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

相关推荐
bobz9654 分钟前
kubeovn with metallb:service externalTraffcLocal
后端
BXCQ_xuan5 分钟前
软件工程实践四:MyBatis-Plus 教程(连接、分页、查询)
spring boot·mysql·json·mybatis
不吃洋葱.8 分钟前
Bean.
java·开发语言
小枫编程10 分钟前
Spring Boot 与前端文件上传跨域问题:Multipart、CORS 与网关配置
前端·spring boot·后端
送秋三十五14 分钟前
spring源码分析————ListableBeanFactory
java·后端·spring
努力也学不会java20 分钟前
【设计模式】状态模式
java·设计模式·状态模式
.豆鲨包22 分钟前
【设计模式】单例模式
java·单例模式·设计模式
Livingbody22 分钟前
【PaddleOCR】基于PaddleOCR V5 最新框架实现车牌识别
后端
邂逅星河浪漫34 分钟前
【Docker+Nginx+Ollama】前后端分离式项目部署(传统打包方式)
java·nginx·docker·部署
一又四分之一.40 分钟前
spring、springboot、springCloud
spring boot·spring·spring cloud