Spring Boot(二):外部配置文件(超级详细)

前言

前面我们说到,springboot使用了特定的方式来进行配置,即约定优于配置(Spring Boot在底层已经把配置都约定(配置)好了)的设计范式,从而使开发人员不再需要定义样板化的配置,就可以正常运行一个spring程序。但是在一些特殊的情况下,我们需要偏离springboot中的约定(默认配置),这时我们就需要自定义一些配置用来修改springboot的默认配置。

一、配置文件的两种类型

Spring Boot 为我们提供一个名称为 application 的全局配置文件,支持两种类型,一种properties类型,一种YAML类型,用于修改 Spring Boot 的默认配置。

两种配置文件

​ application.properties

​ application.yaml 或者 application.yml

在上一篇文章中我们可以看到,当我们创建一个springboot项目的时候,系统默认会为我们在 src/main/java/resources 目录下创建一个 application.properties 配置文件。

因为我个人更喜欢 yaml 格式的配置文件,所以一般在创建好项目后我会将 application.properties 修改为 application.yml。可能我们会在不同的项目中看见两种不同的配置文件格式,实际上两种格式都是OK的。我会在后面详细介绍一下 YAML语言,关于properties就展开来说了。

二、YAML 语言入门

YAML是 YAML Ain't Markup Language(YAML 不是一种标记语言)的递归缩写 ,YAML还可以理解为 Yet Another Markup Language(仍是一种标记语言)。

YAML 是专门用来写配置文件的语言,以数据为中心,非常简洁和强大,比json、xml等更适合做配置文件。

1、基本语法

  • 采用 key: value方式表示数据(冒号后面需要空格隔开)
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释

需要注意的是,YMAL语言只支持单行注释,从 # 开始一直到行尾,都会被解析器忽略。

2、数据类型

YAML 支持以下几种数据类型:

  • 纯量(scalars):单个的、不可再分的值(数字,字符串,布尔。。。)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

下面我们分别对这三种数据类型进行详细的讲解,在这之前先给大家推荐一个在线编辑 yaml 的网站吧,可以将我下面的代码粘进去看结果。不是打广告啊,我这博客有没有人看还不一定,在线卑微。。。在线编辑 yaml

2.1、YAML 纯量

纯量是最基本的,不可再分的值,包括:

  • 字符串

    # 字符串是最常见一种数据类型,默认不用加上单引号或者双引号
    content: 好好学习,天天向上 # == "好好学习,天天向上"
    
    # 如果字符串之中包含空格或特殊字符,需要放在引号之中,其中双引号不会转义字符串,而单引号会
    content: "好好学习\n天天向上" # == "好好学习\n天天向上",会进行换行
    content: '好好学习\n天天向上' # == "好好学习\\n天天向上",不会换行
    
    # 单引号之中如果还有单引号,必须连续使用两个单引号转义
    content: 'YAML Ain''t Markup Language' # == "YAML Ain't Markup Language"
    
    # 字符串可以拆成多行,换行符会被转化成一个空格(转义第二行开始,只需要一个空格)
    content: 小明
     是一个
     大帅哥,谁赞同?谁反对? # == "小明 是一个 大帅哥,谁赞同?谁反对?"
    		
    # 多行字符串可以使用 | 保留换行符,也可以使用 > 折叠换行
    content: | # == "小明\n是一个\n大帅哥,谁赞同?谁反对?\n"
     小明
     是一个
     大帅哥,谁赞同?谁反对?
    content: > # == "小明 是一个 大帅哥,谁赞同?谁反对?\n"
     小明
     是一个
     大帅哥,谁赞同?谁反对?
     
    # 可以使用 |+ 保留文字块末尾的换行,也可以使用 |— 删除字符串末尾的换行
    content: |+ # == "小明\n是一个\n大帅哥,谁赞同?谁反对?\n"
     小明
     是一个
     大帅哥,谁赞同?谁反对?
    content: |- # == "小明\n是一个\n大帅哥,谁赞同?谁反对?"
     小明
     是一个
     大帅哥,谁赞同?谁反对?
     
    # 字符串之中可以插入 HTML
    content: | # == "<p style=\"color: red\">\n  段落\n</p>\n"
     <p style="color: red">
       段落
     </p>
    

    看完这么一大串是不是感觉有点麻烦。。。其实在实际的开发中,我们只会用到最基本的,就我个人而言,引号也就是在配置日志格式和颜色时用到了,后面的换行,换行符什么的,我是一个也没用过。。。

  • 布尔值

    # 布尔值用true和false表示
    falg: true
    isSuccess: false
    
  • 整数、浮点数

    # 数值直接以字面量的形式表示
    age: 20
    age: 0b1010_0111_0100_1010_1110 #二进制表示
    
  • Null

    # null用 ~ 表示
    string: ~ # == "string": null
    
  • 日期、时间

    # 日期和时间都是采用ISO8601的格式表示的
    
    # 日期格式为 yyyy-MM-dd
    createDate: 2020-06-24 # 转换为json为 {"createDate": "2020-06-24T00:00:00.000Z"}
    
    # 时间格式为 时间和日期之间使用T连接,最后使用 +、-(这里不是加减号,是连字符)代表时区
    datetime: 2020-06-24T22:00:00-08:00 # 转换为json为 {"datetime": "2020-06-25T06:00:00.000Z"}
    datetime: 2020-06-24T22:00:00+08:00 # 转换为json为 {"datetime": "2020-06-24T14:00:00.000Z"}
    
    # 从上面的例子可以看出 连字符前面的时间为默认的 0 时区,其后 -08:00 则表示为西八区,而我国则在东八区,用 +08:00 表示
    
2.2、数组

在yaml中,以 - 开头的行则表示成一个数组,例:

calss:
- xiaoming
- xiaohong
- xiaoxiao # == ['xiaoming', 'xiaohong', 'xiaoxiao']

同时,yaml 还支持多维数组,例:

calss:
-
 - xiaoming
 - xiaohong
 - xiaoxiao # == [['xiaoming', 'xiaohong', 'xiaoxiao']]

数组也可以采用行内写法

# 这个和上面的例子表示的意思是一样的
class: [[xiaoming, xiaohong, xiaoxiao]] 
2.3、对象

在 yaml 中一个对象可以使用冒号结构的 key: value 表示,冒号后面要加一个空格。例

city: shanghai # 这个表示一个对象 {city: 'shanghai'},对象中有一个city属性,属性值是shanghai

Yaml 也可以使用 key: {key1: value1, key2: value2, ...},将所有键值对写成一个行内对象。

# 表示一个班级对象,里面有三个属性,班主任、班导、学生
class: {班主任: xiaoming, 班导: xiaohong, 学生: xiaoxiao}

较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value:

? 
    - key1
    - key2
:
    - value1
    - value2 # 意思即对象的属性是一个数组 [key1, key2],对应的值也是一个数组 [value1, value2]
2.4、开发实例

上面我们举了这么多的例子,相信大家对 YAML 语言已经有一定的了解了,下面我们来看看实际开发中 YAML 在 Spring Boot 中的应用。

server:
  # 修改运行端口
  port: 9521
  # 配置项目路径
  servlet:
    context-path: /xm-admin

spring:
  # 配置程序名称
  application:
    name: xm-admin
  # 配置运行环境(dev、test、prod)
  profiles:
    active: dev
  jackson:
    # 设置日期格式化
    date-format: yyyy-MM-dd HH:mm:ss
    # 设置时区
    time-zone: GMT+8

mybatis-plus:
  configuration:
    # 控制台打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

好了,关于 yaml 语言的介绍就到这里结束啦。下面我们来介绍 Spring Boot 如何在配置文件中取值。

三、Spring Boot 配置文件取值

Spring Boot 读取配置文件的常见的方式可以分为

  • 通过 Spring Boot 的环境变量取值
  • 通过注解方式读取

1、通过获取环境变量获取配置

1.1、创建新工程

首先我们需要先创建一个新工程来进行获取配置文件的演示(spring initializr)。

POM文件如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
1.2、编写配置文件

个人习惯先将 application.properties 修改为 application.yml。

在配置文件中随便写点内容:

name: xiaoming
age: 24
hobby:
  - 看电影
  - 听音乐
  - 打游戏
1.3、获取配置内容
1.3.1、启动类获取环境变量获取配置

我们可以通过在启动类中获取到 Spring Boot 上下文对象,从而获取到 Spring Boot 运行的环境变量

@SpringBootApplication
public class AppRun {

    public static void main(String[] args) {
        SpringApplication.run(AppRun.class, args);
    }

}

上面这段代码,相信大家已经很熟悉了,就是一个标准的 Spring Boot 启动类。基本上大家都是这么写的吧,其实我们是可以用一个变量去接收SpringApplication.run(AppRun.class, args);语句的返回值,而返回的对象正是 Spring Boot 的上下文对象。所以我们修改一下代码:

public static void main(String[] args) {
    // 获取 Spring Boot 上下文
    ConfigurableApplicationContext ctx = SpringApplication.run(AppRun.class, args);
    // 获取 Spring Boot 运行的环境变量
    ConfigurableEnvironment ce = ctx.getEnvironment();
    // 通过环境变量获取 字符串
    String name = ce.getProperty("name");
    // 获取 Integer 类型的值
    Integer age = ce.getProperty("age", Integer.class);
    // 获取 数组 类型的值
    String hobby0 = ce.getProperty("hobby[0]", String.class);
    String hobby1 = ce.getProperty("hobby[1]", String.class);
    String hobby2 = ce.getProperty("hobby[2]", String.class);
    
    System.out.println("name:" + name);
    System.out.println("age:" + age);
    System.out.println("hobby[0]:" + hobby0);
    System.out.println("hobby[1]:" + hobby1);
    System.out.println("hobby[2]:" + hobby2);
}

控制台的输出如下:中文配置乱码
从上图可以看到,输出中文的时候会乱码显示。乱码的本质原因就是输入和输出时的编码不一致,所以我们需要修改一下配置文件的编码格式即可解决问题,修改步骤如下:

点击 File -> Settings -> Editor -> File Encodings,修改相关编码格式为UTF-8,建议将 Transparent native-to-ascii conversion 勾选上。

修改完成之后控制台显示的就是正常的中文了。
正常显示中文

1.3.2、注入环境变量获取配置

spring核心库中为我们提供了一个环境变量的接口Environment,我们可以使用@Autowired直接注入到我们的Bean中。

下面我们在 test 包下编写我们的测试类。

@SpringBootTest
class AppRunTests {

    /**
     * 直接注入即可
     */
    @Autowired
    Environment env;

    @Test
    void contextLoads() {
        // 通过直接注入环境变量来获取配置文件的属性
        String name = env.getProperty("name");
        Integer age = env.getProperty("age", Integer.class);
        String hobby0 = env.getProperty("hobby[0]", String.class);
        String hobby1 = env.getProperty("hobby[1]", String.class);
        String hobby2 = env.getProperty("hobby[2]", String.class);

        System.out.println("name:" + name);
        System.out.println("age:" + age);
        System.out.println("hobby[0]:" + hobby0);
        System.out.println("hobby[1]:" + hobby1);
        System.out.println("hobby[2]:" + hobby2);
    }

}

控制台输出的还是和上面的例子是一样的
注入环境变量方式获取配置
通过环境变量获取配置文件的方式我们就讲到这,其实使用环境变量不止能够获取到我们配置文件内容,还能够获取到许多与环境变量有关的值,例如 java.home、java.version、user.name、user.dir 等等一系列的环境变量的默认配置。

2、通过注解获取配置

2.1、@Value 注解

Spring Boot 通过 @Value 注解将配置文件中的属性注入到容器内组件中(可用在@Controller、@Service、@Configuration、@Component等Spring托管的类中),@Value 有两种取值方式 @Value("${}")@Value("#{}")

首先我们来看第一种语法格式,也是比较常用的格式@Value("${}"),这种方式可以直接读取已经被 Spring Boot 加载的配置文件中的属性值,话不多说,我们直接上代码:

首先我们在我们的测试包下新建一个测试类 ConfigTests

@SpringBootTest
public class ConfigTests {

    @Test
    void configTest() {

    }

}

然后就是通过 @Vlaue 注解来获取到 application.yml 中得值了

@SpringBootTest
public class ConfigTests {

    @Value("${name}")
    String name;

    @Value("${age}")
    Integer age;

    /**
     * ${key:value} 这样写表达的含义为 当我们在配置文件中取不到key的值时, value就会填充为默认值
     */
    @Value("${hobby:1}")
    List<String> hobby;

    @Test
    void configTest() {
        System.out.println("name:" + name);
        System.out.println("age:" + age);
        System.out.println("hobby:" + hobby);
    }

}

控制台输出如下内容:

name:xiaoming
age:24
hobby:[1]

疑问:此时我们发现,hobby 的值变成了 [1],而我们的配置文件中的值为 [看电影,听音乐,打游戏] ,为什么我们会取不到 hobby 的值,而使用的默认值 [1] 呢?

我也不知道为什么。。。哈哈(有大佬知道,评论或私信告诉我一下哈,不胜感激),但是我知道怎么解决这个问题。。。请往下看

我们只需要修改一下配置文件中的格式即可:

name: xiaoming
age: 24

# 需要将 list 类型写成由逗号隔开的字符串即可,@Value 会帮我们注入成为 list 类型的值
hobby: 看电影,听音乐,打游戏

#hobby:
#  - 看电影
#  - 听音乐
#  - 打游戏

我们进行debug查看注入的值,可以发现 类型为 ArrayList,size为3,如下图:
在这里插入图片描述
OK,进行完 list 类型的注入,我么继续尝试一下 map 类型的注入,首先在配置文件中添加一个配置:

# 这里需要注意的是,map类型的值需要使用双引号包起来,否则根据 yaml 的语法,会将'{'认为他是一个字典
xiaoming: "{name: 'xiaoming', age: 24, hobby: '看电影,听音乐,打游戏'}"

然后在代码中添加如下语句进行取值,

@Value("${xiaoming}")
Map<String, Object> xiaoming;

此时我们发现,代码报错了,异常如下:

Cannot convert value of type ‘java.lang.String’ to required type ‘java.util.Map’: no matching editors or conversion strategy found

金牌翻译:无法转换’java.lang.String’类型的值到所需类型’java.util.Map’:找不到匹配的编辑器或转换策略

按照字面意思可以看出,@Value("${}") 能够获取到 xiaoming 这一属性的值,只不过获取到的属性值的类型是一个字符串,但是我们就想要获取到一个 map,这个时候怎么办呢?这个时候我们就可以使用第二种语法格式 @Value("#{}") 在结合 @Value("${}") 获取到 map 格式的值。先上代码:

@Value("#{${xiaoming}}") // 需要注意,一定是#{}包括${}
Map<String, Object> xiaoming;

我们继续这个时候就能将配置文件中xiaoming的值注入给map类型了,我们debug进行查看:
在这里插入图片描述

看上图可以发现,age的类型为 Integer ,而hobby的类型为String,其实我本来的想法是能注入成为 list类型,尝试例一下,好像没成功,不知道是我写的有问题,还是@Value不能实现(还是那句话,大佬知道的话,求教!!!)

我们先想想,为什么 @Value("#{${xiaoming}}") 就能使一个 json 字符串转换并注入为 map 类型呢?

因为@Value("#{}") 可以在 #{} 编写SpEl表达式,并将执行结果赋值给属性,所以这种方式注入的是SpEL表达式对应的结果。所以在使用 @Value("#{${xiaoming}}") 的时候其实是使用 SpEL表达式对 @Value("${xiaoming}") 获取到的值再进行处理转换为了map类型的值。

tips:SpEL(Spring Expression Language),即Spring表达式语言,是比JSP的EL更强大的一种表达式语言。

到这里,@Value注解我就介绍完了,就不在细说 @Value("#{}") 的使用场景啦,反正记住一点即可,@Value("${}")注入的是外部配置文件对应的property,而@Value("#{}")则是注入SpEL表达式对应的内容。

接下来我们学习第二个注解(@ConfigurationProperties)

2.2、@ConfigurationProperties注解
2.2.1、思考案例

在学习@ConfigurationProperties注解前,我们先来看这样一个需求,现在我们有如下配置文件,我们需要将其中的全部属性注入到一个 spring bean 中。

# 这是我在项目中使用的alibaba druid数据源的配置
spring:
  # 配置数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/xm_admin?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8
    username: root
    password: root
    # 配置alibaba druid连接池
    druid:
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall,slf4j
      # 最大连接池数量 maxIdle已经不再使用
      maxActive: 20
      # 初始化时建立物理连接的个数
      initialSize: 1
      # 获取连接时最大等待时间,单位毫秒
      maxWait: 60000
      # 最小连接池数量
      minIdle: 1
      # 既作为检测的间隔时间又作为testWhileIdel执行的依据
      timeBetweenEvictionRunsMillis: 60000
      # 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
      minEvictableIdleTimeMillis: 300000
      # 用来检测连接是否有效的sql 必须是一个查询语句 mysql中为 select 'x' oracle中为 select 1 from dual
      validationQuery: select 'x'
      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      testWhileIdle: true
      # 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      testOnBorrow: false
      # 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      testOnReturn: false
      # 是否缓存preparedStatement,也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
      #当值大于0时poolPreparedStatements会自动修改为true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合并多个DruidDataSource的监控数据
      useGlobalDataSourceStat: true
      # 基础监控配置
      web-stat-filter:
        enabled: true
        # 添加过滤规则
        url-pattern: /*
        # 设置忽略哪些URL
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
        session-stat-enable: true
        session-stat-max-count: 100
      #设置视图拦截,访问druid监控页的账号和密码,默认没有
      stat-view-servlet:
        enabled: true
        # 访问路径为/druid时,跳转到StatViewServlet
        url-pattern: /druid/*
        reset-enable: true
        # 设置监控页面的登录名和密码
        login-username: admin
        login-password: admin
        # 白名单ip
        allow: 127.0.0.1
        # 黑名单(共同存在时,deny优先于allow)
        #deny: localhost

这时我们已经学会了两种注入配置文件值的方式,但是看到这段长长的数据源配置是不是蒙圈了,因为不管是使用环境变量的方式,还是@Value的方式,我们都只能一个一个属性的进行注入,现在这个数据源,我们吃点苦还是可以肝完的,但是在我们的实际开发中,我们的配置文件的内容只会比这多得多,要是用@Value的方式,就有点为难我小猪佩奇了。

所以这时候我们就需要用到我们本小节学习的注解@ConfigurationProperties,因为它最大的特点就是可以批量注入配置文件中的属性。上面的几十行配置文件,我们只需要写一个注解就能全部注入。

话不多说,先上代码,最直观的感受一下批量注入属性:

配置文件

user:
  name: xiaoming
  age: 24
  birth: 1996/01/01
  scores: {java: 90, python: 80, scala: 100}
  hobby:
    - 看电影
    - 听音乐
    - 打游戏
  pet:
    species: cat
    name: 星星
    # 单位 月
    age: 8

创建一个 用户配置类 UserConfig

package com.xiaoming.springboot.config;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 用户配置类
 *
 * @author xiaoming
 * @date 2020-07-04 18:31
 **/
@Component
@ConfigurationProperties(prefix = "user")
public class UserConfig {

    private String name;

    private Integer age;

    private Date birth;

    private Map<String, Object> scores;

    private List<String> hobby;

    private Pet pet;

	// 省略 setter getter 方法,实际开发不能省略
}

/**
 * 功能描述: 宠物类
 */
class Pet {
    private String species;
    private String name;
    private Integer age;

    // 省略 setter getter 方法,实际开发不能省略
}

编写测试类方法

// 使用spring注解注入bean
@Autowired
UserConfig userConfig;

@Test
void userTest() {
	System.out.println(userConfig.toString());
}

查看控制台输出:

UserConfig{name='19472', age=24, birth=Mon Jan 01 00:00:00 CST 1996, 
scores={java=90, python=80, scala=100}, hobby=[看电影, 听音乐, 打游戏], 
pet=Pet{species='cat', name='星星', age=8}}

到这里我们可以看到,配置文件中的属性已经全部注入进去,只不过我们的name属性好像不太对劲,我们配置文件中的值是 xiaoming,而19472是我电脑的当前用户名,这是因为user.name正好是系统环境变量中的一个属性,根据配置文件加载顺序,系统环境变量优先级高于配置文件属性,而被覆盖,关于配置文件加载顺序,文章的后面会进行讲解。

2.2.2、作用和注意点

看完上面的案例,我们再来详细介绍一下 @ConfigurationProperties注解

@ConfigurationProperties可以将外部配置文件(比如applicaition.properties)加载进来,填充对象的对应字段的数据,然后供其他Bean使用。

注意点:

  1. 在bean使用@ConfigurationProperties,这个bean需要被 spring boot 扫描并加载到容器中,这一点与@Value相似,所以我在bean上面加了@Component注解
  2. 这个bean中的字段必须有公共 setter 方法,否则不能注入进来
  3. 根据 Spring Boot 宽松的绑定规则,类的属性名称必须与外部属性的名称匹配
  4. 前缀定义了哪些外部属性将绑定到类的字段上
  5. @ConfigurationProperties不仅可以作用于类上,还可以标注在方法上
  6. 使用这个 bean ,需要使用@Autowired 注入的方式使用
2.2.3、和@Value注解的异同点
@ConfigurationProperties@Value
注入方式批量注入单个注入
SpEL语法不支持支持
松散绑定支持不支持
JSR303数据校验支持不支持
复杂类型注入支持不支持

在上面的对比表格中,注入方式,SpEL语法,和复杂类型注入三点我们在前面的介绍中都已经说过了,虽然不是单独拿出来说的,这里就不解释了,但是还有两个我们比较陌生的词(松散绑定,JSR303数据校验),这两个我们在下面拿出来单独介绍一下。

2.2.4、松散绑定(Relaxed Binding)

官网介绍:Spring Boot uses some relaxed rules for binding properties to beans, so there does not need to be an exact match between the property name and the bean property name

金牌翻译:springboot使用一些宽松的规则将属性绑定到bean,因此属性名和bean属性名之间不需要完全匹配

同样,引用官网的案例,如下:

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

使用上述代码时,可以使用以下属性名称:

PropertyNote
acme.my-project.person.first-name短横线隔开式,建议在.properties.yml文件中使用。
acme.myProject.person.firstName标准驼峰命名法。
acme.my_project.person.first_name下划线表示法,这是用于.properties.yml的备用格式。
ACME_MYPROJECT_PERSON_FIRSTNAME大写格式,建议使用系统环境变量时使用大写格式。

可以看到,上面4中配置文件书写方式都能够将属性值注入到bean中,而不需要完全匹配属性值名称,这就是松散绑定。

2.2.5、JSR303数据校验

首先我们先来介绍一下 JSR 303。

JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

Bean Validation 规范内嵌的约束注解

限制说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

下面进入主题,介绍@ConfigurationProperties注入配置文件属性是的数据校验

还是借用官网的话吧(大晚上的,写累了。。。)

Spring Boot attempts to validate @ConfigurationProperties classes whenever they are annotated with Spring’s @Validated annotation. You can use JSR-303 javax.validation constraint annotations directly on your configuration class. To do so, ensure that a compliant JSR-303 implementation is on your classpath and then add constraint annotations to your fields

金牌翻译:只要@ConfigurationProperties类被Spring的@Validated注解标注时,Spring Boot 都会尝试验证该类。您可以使用JSR-303直接对配置类进行约束注释。为此,请确保符合的 JSR-303 实现位于类路径上,然后向字段添加约束注释。

官网案例代码:

@ConfigurationProperties(prefix="acme")
// 这个注解用来开启JSR 303验证
@Validated
public class AcmeProperties {

    // 这个注解是 JSR 303 内嵌的约束注解,表示不能为null
    @NotNull
    private InetAddress remoteAddress;

    // 为了确保始终触发对注入属性的验证,即使找不到属性,关联字段也必须用@Valid注释
    @Valid
    private final Security security = new Security();

    // ... getters and setters

    public static class Security {

        @NotEmpty
        public String username;

        // ... getters and setters

    }

}

到这里,@ConfigurationProperties就介绍的差不多了吧,现在脑子有点混乱。。。哈哈,写了一天也没写多少知识点,这效率得加油鸭!!!

3、小结

关于配置文件取值,我们介绍了最常见的三种方式,还有什么PropertiesLoaderUtilsYamlPropertiesFactoryBeanYamlMapFactoryBean(后面两个类是spring官方提供的两个读取yml文件的类,也是很好用的)等等方式,我就不详细介绍了,关于介绍了的三种方式,最最常用的还是两个注解,在项目开发中,我暂时还没有用过使用环境变量的方式(也可能是我段位太低哈哈,小声逼逼),一般来说都是两个注解根据实际需求结合使用,开发效率杠杠滴。

四、Spring Boot 多环境配置

Spring Boot 可以利用 yaml 文件实现多环境配置(Multi-profile YAML Documents),有两种实现方式:

  1. 在单个 yml 中编写多个环境配置
  2. 编写多个yml文件,分别代表不同的环境配置

1、单yml文件

yaml 支持多文档块的方式,通过 --- 分隔不同的文档块,所以我们可以利用这一点,在 application.yml 文件中编写多个文档块,编写多个文档块的时候,需要使用然后使用 spring.profiles 标注当前文档块的名称 , spring.profiles.active 属性的值,来激活哪个文档块会被 springboot 加载。

直接上代码(application.yml):

spring:
  profiles:
    # 指定我们需要激活的文档块,如果不指定的话,默认为 default
    active: dev

---
server:
  # 程序启动端口
  port: 9520
spring:
  # 文档块的标识
  profiles: default

---
server:
  port: 9521
spring:
  profiles: dev

---
server:
  port: 9527
spring:
  profiles: pro

首先我们可以看出,上面的配置文件中存在4个文档块,我们可以在 IDEA 中查看,如下图:
在这里插入图片描述
在yml配置文件底部会存在一个面包屑导航,让我们更直观看到配置文件的层级关系。

下面我们运行程序,验证一下启动端口是不是 9521

我们需要添加web启动器,springboot会自动帮我们装配好 Tomcat 容器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

启动程序查看启动日志:
在这里插入图片描述
从上图可以看出两个信息,我们的 active-profile 就是我们指定的 dev,同时启动端口也是 9521。所以证明了我们的第一种写法完全是没有问题的。

需要注意的是,我们如果不指定spring.profiles.active的值,那么就会使用default名称的文档块,如果没有该文档块,就使用默认的文档块,程序启动日志:No active profile set, falling back to default profiles: default

2、多yml文件

在开发中,一般编写多环境配置文件都是使用这种方式,因为在实际开发中,配置文件的属性一般比较多,如果多环境的配置都写在一个文件中,则会显得很臃肿,不好维护,所以,我们选择将不同环境的配置,编写到不同的配置文件中。

这种方式和单yml文件的方式,除了编写格式有区别之外,其他基本一致。单yml文件在区别不同文档环境时,使用spring.profile属性区别,而多yml文件是不需要配置这个属性的,我们只需要在创建配置文件的时候在文件名符合 application-{profile}.yml格式即可,即以文件名的方式区分不同环境,如 application-dev.yml

下面我们修改一下代码:

首先新建两个配置文件 application-dev.yml 和 application-pro.yml,如何将 application.yml 中的多余文档块注释掉

application.yml 代码

spring:
  profiles:
    # 指定我们需要激活的文档块,如果不指定的话,默认为 default
    active: pro

application-dev.yml 代码

server:
  port: 9521

application-pro.yml 代码

server:
  port: 9527

启动程序,查看日志,盲猜启动端口为 9527
在这里插入图片描述

果然不出意外端口是9527(手动狗头),所以这也验证了第二种书写方式也是完全OK的。到这里 YAML 多环境配置就介绍完了,springboot 官网就介绍第一种方式,我这里给各位赠送了第二种最常用的方式,大家确定不给我点赞吗,哈哈。

tips:根据配置文件加载优先级(下一节就讲),我们可以在工程打成jar包后,通过java命令行的方式制定需要加载的配置环境,而不用每次都修改配置打新包,例如:java -jar spring-boot-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

五、Spring Boot 加载配置文件

待更新(2020-07-05)。。。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值