Spring Boot 外部化配置 - 上篇

Spring Boot 外部化配置 - 上篇

文章说明

本系列会完整的介绍 Spring Boot 中外部化配置相关应用以及部分源码分析

  • 上篇 - 什么是外部化配置,外部化配置有哪些应用,顺序覆盖性
  • 中篇 - @Value 注入、Environment 抽象、@ConfigurationProperties、@ConditionalOnProperty 应用
  • 下篇 - 如何扩展外部化配置,代码演示覆盖顺序

项目环境

1.Spring Boot 官方说明

官网文档地址:https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/html/boot-features-external-config.html

24. Externalized Configuration

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration. Property values can be injected directly into your beans by using the @Value annotation, accessed through Spring’s Environment abstraction, or be bound to structured objects through @ConfigurationProperties.

翻译:Spring Boot 让你可以外部化你的配置,以便于你可以让相同的应用在不同的环境中可以正常工作。你可以使用 properties、YAML 、环境变量和命令行参数去外部化配置,这里还举了三个使用方式

  • @Value 注解
  • Environment 抽象
  • @ConfigurationProperties

2.举例说明

这里我们可以举一个简单的例子比如

使用 application.properties 去调整 Tomcat 的端口号

通常我们的默认端口是 8080,但是为了防止端口冲突,通常我们会修改 application.properties 文件增加如下配置即可,表示应用以 8085 端口启动,application.properties 文件并非程序代码部分,所以称为外部化配置。

server.port = 8085

小马哥(《Spring Boot 编程思想》作者)解读

何为 外部化配置?通常,对于可扩展性应用,尤其是中间件,他们的功能性组件是可配置化的,如:认证信息、端口范围、线程池规模以及连接时间等等。

假设需要设置 Spring 应用的 Profiledev,可通过调整 Spring ConfigurableEnvironmentsetActiveProfiles("dev") 方法实现。这种方式是一种显示的代码配置,配置数据来源于应用内部实现,所以称为 内部化配置,虽然能达成目的,但是缺少相应的弹性。

那么相对应的就是外部化配置,比如:application.properties 加入如下配置

spring.profiles.active = dev

3.应用外部化配置

本章只介绍 XML Bean 这种方式,其他的 4 种在 Spring Boot 外部化配置 - 中篇 进行详细讨论

应用场景

  • XML Bean 定义的属性占位符
  • @Value 注入 - @since SpringFramework 3.0
  • Environment 读取 - @since SpringFramework 3.1
  • @ConfigurationProperties 绑定(Spring Boot 中新增注解)
  • @ConditionalOnProperty 判断(Spring Boot 中新增注解)

4 XML Bean 定义的属性占位符

4.1 传统 spring 例子

在 resources/META-INF/spring 下新建 spring-context.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 属性占位符配置-->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><!-- Properties 文件 classpath 路径 -->
        <property name="location" value="classpath:/META-INF/default.properties"/><!-- 文件字符编码 -->
        <property name="fileEncoding" value="UTF-8"/>
    </bean>
</beans>

user-context.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- User Bean -->
    <bean id="user" class="com.huajie.deepinspringboot.externlized.configuration.domain.User">
        <property name="id" value="${user.id}"/>
        <property name="name" value="${user.name}"/>
    </bean>

</beans>

在 resources/META-INF 下创建

default.properties

user.id = 1
user.name = 小仙

新建 User 类

public class User {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

引导类 SpringXmlConfigPlaceHolderBootstrap

public class SpringXmlConfigPlaceHolderBootstrap {

    public static void main(String[] args) {
        String[] locations = new String[]{"META-INF/spring/spring-context.xml", "META-INF/spring/user-context.xml"};
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(locations);
        User user = applicationContext.getBean("user",User.class);

        System.out.println(user);

        applicationContext.close();
    }

}

执行结果

User{id=1, name='小仙'}

4.2 Spring Boot 例子

修改 spring-context.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 属性占位符配置-->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="fileEncoding" value="UTF-8"/>
        <!-- 替换为 Spring Boot 中的 application.properties -->
        <property name="location" value="classpath:application.properties"/>
    </bean>
</beans>

resources 目录下新增 application.properties 文件

user.id = 1
user.name = 小仙2020

引导类

@ImportResource("META-INF/spring/user-context.xml")
@EnableAutoConfiguration
public class XmlPlaceHolderExternalizedConfigurationBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(XmlPlaceHolderExternalizedConfigurationBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        User user = applicationContext.getBean("user",User.class);

        System.out.println(user);

        applicationContext.close();

    }
}

执行结果:

User{id=1, name='Administrator'}

结果并非我们配置的 小仙2020,而是 Administrator,问题出在哪里呢?

改造上面的引导类,

@ImportResource("META-INF/spring/user-context.xml")
@EnableAutoConfiguration
public class XmlPlaceHolderExternalizedConfigurationBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(XmlPlaceHolderExternalizedConfigurationBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        User user = applicationContext.getBean("user",User.class);

        System.out.println(user);

        System.out.println("系统变量:"+System.getProperty("user.name"));

        applicationContext.close();
    }
}

执行结果:

User{id=1, name='Administrator'}
系统变量:Administrator

其实打印的 user.name 属性是 Java 系统变量,而并不是我们预期的 application. properties 文件中配置的属性。

4.3 外部化配置顺序覆盖问题

这里我们可以看 Spring Boot 官方文档

https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/html/boot-features-external-config.html

24. Externalized Configuration

Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:

  1. Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
  2. @TestPropertySource annotations on your tests.
  3. @SpringBootTest#properties annotation attribute on your tests.
  4. Command line arguments.
  5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
  6. ServletConfig init parameters.
  7. ServletContext init parameters.
  8. JNDI attributes from java:comp/env.
  9. Java System properties (System.getProperties()).
  10. OS environment variables.
  11. A RandomValuePropertySource that has properties only in random.*.
  12. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
  13. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
  14. Application properties outside of your packaged jar (application.properties and YAML variants).
  15. Application properties packaged inside your jar (application.properties and YAML variants).
  16. @PropertySource annotations on your @Configuration classes.
  17. Default properties (specified by setting SpringApplication.setDefaultProperties).

外部化配置有个顺序的覆盖问题,前面的配置属性可以覆盖后面配置属性。

我们的 application.properties 文件排名 13,而 Java System properties 的顺序是 9,所以 Java 系统变量 user.name 覆盖了我们 application.properties 中配置的属性。

为了验证这个顺序的覆盖性,我们再测试一种配置方式 4.Command line arguments.

在启动程序中配置命令行的参数--user.name=xwf
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QlFHERtH-1590196915756)(G:\workspace\csdn\learn-document\spring-boot\慕课课程\csdn\image-20200520144728171.png)]

启动执行结果:

User{id=1, name='xwf'}
系统变量:Administrator

可以看到输出的 User 对象 name 属性变成了 xwf,验证了我们的猜想。

5.参考

  • 慕课网-小马哥《Spring Boot2.0深度实践之核心技术篇》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值