SpringBoot中的配置
这里所说的配置是指properties文件这样的配置
配置的方式及优先级
Spring Boot允许通过properties文件, YAML文件, Environment变量, 命令行参数等进行配置. 属性值可以通过@Value注入到bean中并通过Spring的Environment访问, 或通过@ConfigurationProperties直接绑定到对象上.
Spring Boot所提供的配置优先级从高到低如下所示:
- Spring的devtools的全局配置(~/.spring-boot-devtools.properties文件)(当使用了devtools时)
- Test类上通过@TestPropertySource声明的属性文件
- Test类上通过@SpringBootTest#properties声明的属性
- 命令行参数
- SPRING_APPLICATION_JSON属性, 环境变量或系统属性中的JSON
- ServletConfig初始化参数
- ServletContext初始化参数
- 来自于java:comp/env的JNDI属性
- Java系统属性(System.getProperties())
- 操作系统环境变量
- 通过RandomValuePropertySource生成的random.*属性
- jar包外的profile配置文件(application-{profile}.properties和YAML配置)
- jar包内的profile配置文件(application-{profile}.properties和YAML配置)
- jar包外的应用程序配置文件(application.properties和YAML配置)
- jar包内的应用程序配置文件(application.properties和YAML配置)
- 配置类(@Configuration类)上的通过@PropertySource注解声明的属性文件
- 通过SpringApplication.setDefaultProperties声明的默认属性
优先级举例
在classpath:application.properties文件里有个name变量(假设将它打成了jar包), 当在一个新的环境中运行时, 可以通过在jar包外(即新环境的的classpath下)提供一个application.properties文件, 重新设置name变量的值. 甚至在测试的时候,可以通过优先级更高的命令行参数指定name的值(java -jar app.jar --name="Spring")
命令行参数
SpringApplication会把所有的命令行参数(以--开头, 如--server.port=9000)转化为属性加载到Spring的Environment中, 命令行参数的优先级高于配置文件
如果不想让命令行参数添加到Environment中, 可通过SpringApplication.setAddCommandLineProperties(false)设置
SPRING_APPLICATION_JSON
上面第5条中说的SPRING_APPLICATION_JSON属性, 可以在命令行中指定
$ SPRING_APPLICATION_JSON='{"foo":{"bar":"spam"}}' java -jar myapp.jar  // 环境变量形式这样就相当于在Spring的Environment中添加了foo.bar=spam.
 也可以像下面这些方式提供:
$ java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar   // 系统变量
$ java -jar myapp.jar --spring.application.json='{"foo":"bar"}'   // 命令行参数或以JNDI变量java:comp/env/spring.application.json提供
其实上面介绍的这几条优先级比较高的配置, 实际并不太常用. 命令行在测试的时候用的还算比较多
配置文件:application.properties
SpringApplication默认会加载配置文件application.properties中的配置并加到Spring Environment中, 该文件的加载有个优先级: classpath:/config/application.properties > classpath:/application.properties即在classpath:/config/下的配置文件优先级比较高. 也可以使用YAML文件(application.yml)来替代properties文件.
application.properties被称为Spring Boot的外露配置, 文件中有很多属性可用来配置整个应用, 比如server.port=8080等; 你可以通过指定这些属性值来配置应用.
配置文件的名字和位置, 也可自定义, 可通过spring.config.name和spring.config.location环境属性来指定, 这两个属性使用的时期非常早, 所以一般会在命令行或者系统属性或环境变量中来指定, 如:
$ java -jar myproject.jar --spring.config.name=myproject
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties若spring.config.location指定的是一个目录, 则应该以/结尾, 并且使用该目录下spring.config.name指定的配置文件
随机变量
RandomValuePropertySource可以注入一些随机变量, 可产生integer, long, string, uuid等类型的随机值, 例如
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}random.int*的语法为OPEN value (,max) CLOSE, OPEN,CLOSE是字符, value,max是整数. 如果有max则最小值是value最大值是max(不包括max).
变量引用
application.properties中定义的变量已经被Environment过滤, 所以可以引用前面定义过的变量, 比如:
app.name=MyApp
app.description=${app.name} is a Spring Boot application多环境配置
- 从配置的优先级的第12~15条可以看出, application-{profile}.properties的优先级要高于application.properties.
- 这个profile就用于区分是dev环境还是beta环境还是prod环境. 如果没有被指定, 默认会使用application-default.properties配置.
- 至于到底启用哪个profile, 可以在application.properties中通过属性spring.profiles.active=profile来指定, 在profile配置文件中指定该属性不起作用.
举个例子, application.properties中有个默认属性server.port=8080用于指定服务的端口. 假设有下面的文件, 文件内容如下:
// application.properties
server.port=8080
spring.profiles.active=dev
// application-default.properties
server.port=8081
// application-dev.properties
server.port=8082
// application-prod.properties
server.port=8083假设application.properties中不指定spring.profiles.active属性, 则application-default.properties中的8081端口生效, 若指定spring.profiles.active=prod, 则8083端口生效. 访问8080端口都会找不到服务
自定义配置
Spring Boot默认加载application.properties中的配置, 这个文件中的默认属性相当多...
 如果我们要加载自己的配置, 比如下面的数据库配置:
db.driver=MySQL
db.username=username
db.password=123456
db.tables[0]=table1
db.tables[1]=table2可以把这些属性直接放到application.properties中, 但极力不推荐这样.
传统的配置加载方式
我们一般都是定义自己的配置文件, 比如把这些属性放到db.properties文件. 然后通过@PropertySource加载配置文件, 然后通过@Value("${key:defaultVlaue}")的形式进行配置, 如下:
@Component
@PropertySource("db.properties")
public class DBConfig {
    @Value("${db.driver}")
    private String driver;
    @Value("${db.username}")
    private String username;
    @Value("${db.password}")
    private String password;
    @Value("${db.tables[0]}")
    private String table1;
    @Value("${db.tables[1]}")
    private String table2;
}注: properties文件默认是按照unicode加载, 若有中文, 一定要指定编码@PropertySource(value = "db.properties", encoding = "UTF-8")
类型安全的配置加载方式
上面这种方式在Spring Framework普遍使用, 但是 Spring Boot提供了更高级的使用配置的方式,类似于Spring中的DataBinder工具. 还是db.properties文件, 我们可以这样进行数据绑定:
@Data
@Component
@ConfigurationProperties(prefix="db", locations = "classpath:db.properties")
public class DBConfig {
    private String driver;
    private String username;
    private String password;
    private List<String> tables;
}最上面的@Data是Lombok包中用于生成getter, setter等的注解, pom依赖为:
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>不用这个包也可以, 那就需要自己写 getter和setter方法了
另外注意此时该类上是加了@Component注解的, 这样才会被当作Spring的Bean.
其实不在DBConfig上加@Component注解也有办法, 通常@ConfigurationProperties是和@EnableConfigurationProperties一起使用的, @EnableConfigurationProperties注解需要加到配置类上. 像下面这样使用:
// 配置类
@SpringBootApplication
@EnableConfigurationProperties({DBConfig.class})
public class Application {
  // 代码
}
// 加载属性的类(主意这个类没有加 @Component 注解)
@ConfigurationProperties(prefix="db", locations = "classpath:db.properties")
public class DBConfig {
  // 代码
}这种形式, @ConfigurationPropertiesbean将会以名字<prefix>-<fqn>注册, <prefix>就是注解中指定的前缀, <fqn>是该类的全类名. 上面的DBConfig将会被注册成名字为db-com.example.myproject.config.DBConfig的bean
@ConfigurationProperties的优缺点
优点:
- 结构化, 对于结构化的配置, 优势明显
- 松散绑定, Environment属性名和@ConfigurationProperties Beans属性名不需要精确匹配, 比如驼峰person.firstName, 虚线pserson.first-name, 下划线person.first_name, 大写PERSON_FIRST_NAME都能正确区分绑定
- 可校验, 可以在属性上添加@NotNull,@NotEmpty等(JSR-303)注解进行校验
- 可生成meta-data文件(可被IDE使用)
缺点:
- 不支持SpEL表达式
使用YAML配置
YAML是JSON的超集, 有一定的结构, SpringApplication提供了对YAML的支持. 使用YAML配置文件需要确保在classpath中引入了SnakeYAML包, spring-boot-starter中已经包含了SnakeYAML包, 也可以主动显式地添加pom依赖:
<dependency>
	  <groupId>org.yaml</groupId>
		<artifactId>snakeyaml</artifactId>
		<version>1.17</version>
</dependency>加载application.yml
Spring Boot会自动加载这个配置, 因此效果跟application.properties一样。
 Spring 提供了两个方便的类加载YAML, YamlPropertiesFactoryBean把YAML作为Properties加载, YamlMapFactoryBean把YAML作为Map加载;YamlPropertySourceLoader可以把YAML作为PropertySource加到Spring Environment中, 这样就可以用@Value的方式进行注入了.
比如下面的写法是一样的
// yml文件
environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App
my:
    servers:
        - dev.bar.com
        - foo.bar.com
// properties文件
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com加载自定义YAML配置
遗憾的是, YAML不能像properties文件一样使用@PropertySource注解的方式加载.
 加载自定义的YAML文件可以通过@ConfigurationProperties注解来加载, 如:@ConfigurationProperties(prefix="db", locations = "classpath:db.yml")
