69. 属性&配置

69.1. 运行时暴露属性

相对于在项目构建配置中硬编码某些配置,你可以使用已存在的构建配置自动暴露它们,Maven和Gradle都支持。

69.1.1. 使用Maven自动暴露属性

你可以使用Maven的资源过滤(resource filter)自动暴露来自Maven项目的属性,如果使用spring-boot-starter-parent,你可以通过@..@占位符引用Maven项目的属性,例如:

[email protected]@
[email protected]@

如果启用addResources标识,spring-boot:run可以将src/main/resources直接添加到classpath(出于热加载目的),这就绕过了资源过滤和本特性。你可以使用exec:java目标进行替代,或自定义该插件的配置,具体查看插件使用页面

如果不使用starter parent,你需要将以下片段添加到pom.xml中(<build/>元素内):

<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
    </resource>
</resources>

和(<plugins/>元素内):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.7</version>
    <configuration>
        <delimiters>
            <delimiter>@</delimiter>
        </delimiters>
        <useDefaultDelimiters>false</useDefaultDelimiters>
    </configuration>
</plugin>

如果你在配置中使用标准的Spring占位符(比如${foo})且没有将useDefaultDelimiters属性设置为false,那构建时这些属性将被暴露出去。

69.1.2. 使用Gradle自动暴露属性

你可以通过配置Java插件的processResources任务自动暴露来自Gradle项目的属性:

processResources {
    expand(project.properties)
}

然后你可以通过占位符引用Gradle项目的属性:

app.name=${name}
app.description=${description}

Gradle的expand方法使用Groovy的SimpleTemplateEngine转换${..}占位符,${..}这种格式跟Spring自身的属性占位符机制冲突,想要自动暴露Spring属性占位符,你需要将其进行编码,比如\${..}

69.2. 外部化SpringApplication配置

SpringApplication已经被属性化(主要是setters),所以你可以在创建应用时使用它的Java API修改其行为,或者使用以spring.main.*为key的属性来外部化这些配置。比如,在application.properties中可能会有以下内容:

spring.main.web-environment=false
spring.main.banner-mode=off

这样,Spring Boot在启动时将不会显示banner,并且该应用也不是一个web应用。

以上示例也展示在属性名中使用下划线(_)和中划线(-)的灵活绑定。

外部配置定义的属性会覆盖创建ApplicationContext时通过Java API指定的值,让我们看如下应用:

new SpringApplicationBuilder()
    .bannerMode(Banner.Mode.OFF)
    .sources(demo.MyApp.class)
    .run(args);

并使用以下配置:

spring.main.sources=com.acme.Config,com.acme.ExtraConfig
spring.main.banner-mode=console

实际的应用将显示banner(被配置覆盖),并为ApplicationContext指定3个sources,依次为:demo.MyAppcom.acme.Configcom.acme.ExtraConfig

69.3 改变应用程序外部配置文件的位置

默认情况下,来自不同源的属性以一个定义好的顺序添加到Spring的Environment中(精确顺序可查看'Sprin Boot特性'章节的Chapter 24, Externalized Configuration)。

为应用程序源添加@PropertySource注解是一种很好的添加和修改源顺序的方法。传递给SpringApplication静态便利设施(convenience)方法的类和使用setSources()添加的类都会被检查,以查看它们是否有@PropertySources,如果有,这些属性会被尽可能早的添加到Environment里,以确保ApplicationContext生命周期的所有阶段都能使用。以这种方式添加的属性优先级低于任何使用默认位置(比如application.properties)添加的属性,系统属性,环境变量或命令行参数。

你也可以提供系统属性(或环境变量)来改变该行为:

  • spring.config.nameSPRING_CONFIG_NAME)是根文件名,默认为application
  • spring.config.locationSPRING_CONFIG_LOCATION)是要加载的文件(例如,一个classpath资源或URL)。Spring Boot为该文档设置一个单独的Environment属性,它可以被系统属性,环境变量或命令行参数覆盖。

不管你在environment设置什么,Spring Boot都将加载上面讨论过的application.properties。如果使用YAML,那具有.yml扩展的文件默认也会被添加到该列表,详情参考ConfigFileApplicationListener

69.4 使用'short'命令行参数

有些人喜欢使用(例如)--port=9000代替--server.port=9000来设置命令行配置属性。你可以通过在application.properties中使用占位符来启用该功能,比如:

server.port=${port:8080}

如果你继承自spring-boot-starter-parent POM,为了防止和Spring格式的占位符产生冲突,maven-resources-plugins默认的过滤令牌(filter token)已经从${*}变为@(即@maven.token@代替${maven.token})。如果直接启用maven对application.properties的过滤,你可能想使用其他的分隔符替换默认的过滤令牌。

在这种特殊的情况下,端口绑定能够在一个PaaS环境下工作,比如Heroku和Cloud Foundry,因为在这两个平台中PORT环境变量是自动设置的,并且Spring能够绑定Environment属性的大写同义词。

69.5 使用YAML配置外部属性

YAML是JSON的一个超集,可以非常方便的将外部配置以层次结构形式存储起来,比如:

spring:
    application:
        name: cruncher
    datasource:
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost/test
server:
    port: 9000

创建一个application.yml文件,将它放到classpath的根目录下,并添加snakeyaml依赖(Maven坐标为org.yaml:snakeyaml,如果你使用spring-boot-starter那就已经包含了)。一个YAML文件会被解析为一个Java Map<String,Object>(和一个JSON对象类似),Spring Boot会平伸该map,这样它就只有1级深度,并且有period-separated的keys,跟人们在Java中经常使用的Properties文件非常类似。 上面的YAML示例对应于下面的application.properties文件:

spring.application.name=cruncher
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000

查看'Spring Boot特性'章节的Section 24.6, “Using YAML instead of Properties”可以获取更多关于YAML的信息。

69.6 设置生效的Spring profiles

Spring Environment有一个API可以设置生效的profiles,但通常你会通过系统属性(spring.profiles.active)或OS环境变量(SPRING_PROFILES_ACTIVE)设置。比如,使用一个-D参数启动应用程序(记着把它放到main类或jar文件之前):

$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar

在Spring Boot中,你也可以在application.properties里设置生效的profile,例如:

spring.profiles.active=production

通过这种方式设置的值会被系统属性或环境变量替换,但不会被SpringApplicationBuilder.profiles()方法替换。因此,后面的Java API可用来在不改变默认设置的情况下增加profiles。

想要获取更多信息可查看'Spring Boot特性'章节的Chapter 25, Profiles

69.7 根据环境改变配置

一个YAML文件实际上是一系列以---线分割的文档,每个文档都被单独解析为一个平坦的(flattened)map。

如果一个YAML文档包含一个spring.profiles关键字,那profiles的值(以逗号分割的profiles列表)将被传入Spring的Environment.acceptsProfiles()方法,并且如果这些profiles的任何一个被激活,对应的文档被包含到最终的合并中(否则不会)。

示例:

server:
    port: 9000
---

spring:
    profiles: development
server:
    port: 9001

---

spring:
    profiles: production
server:
    port: 0

在这个示例中,默认的端口是9000,但如果Spring profile development生效则该端口是9001,如果production生效则它是0

YAML文档以它们出现的顺序合并,所以后面的值会覆盖前面的值。

想要使用profiles文件完成同样的操作,你可以使用application-${profile}.properties指定特殊的,profile相关的值。

69.8 发现外部属性的内置选项

Spring Boot在运行时会将来自application.properties(或.yml)的外部属性绑定到应用,因为不可能将所有支持的属性放到一个地方,classpath下的其他jar也有支持的属性。

每个运行中且有Actuator特性的应用都会有一个configprops端点,它能够展示所有边界和可通过@ConfigurationProperties绑定的属性。

附录中包含一个application.properties示例,它列举了Spring Boot支持的大多数常用属性,查看@ConfigurationProperties@Value,还有不经常使用的RelaxedEnvironment的源码可获取最权威的属性列表。