Spring boot reference guide 23-25

Part IV. Spring Boot Feature

23. SpringApplication

默认情况下,spring boot 程序启动会显示INFO级别的日志信息,

23.2 自定义banner

可以在classpath 下面添加一个banner.txt 文件自定义项目启动过程中打印的banner,或者使用spring.banner.location指定banner文件的位置。如果banner文件的编码不是UTF-8,可以使用spring.banner.charset指定。不仅是txt文件,还可以在classpath下使用banner.gif,banner.jpg,banner.png,或者使用sping.banner.image.location指定图片的路径。图片会转换为字符画(ASCII art)形式,并在所有文本banner上方显示。

TIP
如果想使用程序创建一个banner,可以使用SpringApplication.setBanner(...),实现org.springframework.boot.Banner接口并且实现printBanner()方法。

spring.main.banner-mode属性,有三种取值,决定banner 的打印位置。

  • console:System.out
  • log : configured logger
  • off : 都不

打印的banner以单例的形式注册到spring容器中,bean名称为springBootBanner

Note
YAML将off映射到false,如果需要在yml文件中禁用banner,需要在off加上引号。如下所示:

    spring:
        main:
            banner-mode: "off"

23.3 自定义SpringApplication

如果SpringApplication不能满足你的要求,可以创建一个本地实例进行自定义。比如,想关闭banner

public static void main(String[] args) {
    SpringApplication app=new SpringApplication (MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);    
    app.run(args);
}

传递给SpringApplication的构造器参数将作为spring beans的配置源,多数情况下,它们是一些@Configuration类的引用,但也可能是XML配置或要扫描包的引用

NODE
springapplicaton的完整配置,参考springapplication Javadoc

23.4 流式构建API

如果需要创建一个分层的ApplicationContext(多个具有父子关系的上下文),或只是喜欢使用流式(fluent)构建API,那你可以使用SpringApplicationBuilder。 SpringApplicationBuilder允许你以链式方式调用多个方法,包括parent和child方法,这样就可以创建多层次结构,例如:

    new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

23.5 事件和监听器

除了spring framework 常规的事件(比如 ContextRefreshedEvent)以外,SpringApplication还会发送一些额外的事件。 、

Note
有一些事件是在ApplicatoinContext之前就被创建的,所以不能在那些事件上使用@Bean的方式注册监听器。有另外的中两种方式可以注册监听器,springapplication.addlisteners()或者使用springapplicationbuilder.listeners()方法。
可以在META-INF/spring.factories指定org.springframework.context.ApplicationListener值,自动添加监听器,而不去管程序是如何创建的,比如:

org.springframework.context.ApplicationListener=com.example.MyListener

当程序启动的时候,程序事件按照如下顺序发送:

  1. 在运行开始,但除了监听器注册和初始化以外的任何处理之前,会发送一个ApplicationStartingEvent。
  2. 当获取用于上下文的Environment之后,上下文被创建之前,会发送一个ApplicationEnvironmentPreparedEvent。
  3. bean definitions被加载之后,refresh之前,发送一个ApplicationPreparedEvent。
  4. 上下文刷新之后,任何程序或者命令行调用之前,发送一个ApplicationStartedEvent。
  5. 任何程序或者命令行被调用之后,发送一个ApplicationReadyEvent,它表明应用程序已经准备好为请求提供服务。
  6. 如果启动过程中出现异常,会发送一个ApplicationFailedEvent

程序事件是通过spring framework事件发送机制发送的,这些机制的部分会保证 发向位于child context 中的监听器的时间,同样会被发向 位于 ancestor context 中的监听器。导致的后果就是,如果你的程序中使用了层级的springApplication实例,一个监听器可能会监听到多个相同类型的程序事件。
为了让监听器正确区分一个事件是来自于他的context还是他的descendant context,应该注入他的application context,然后同事件的context进行比较。要想注入context,context需要实现ApplicationContextAware结果,或者如果context是一个bean,使用@Autowired.
说实话,没懂
Application events are sent by using Spring Framework’s event publishing mechanism. Part of this
mechanism ensures that an event published to the listeners in a child context is also published
to the listeners in any ancestor contexts. As a result of this, if your application uses a hierarchy
of SpringApplication instances, a listener may receive multiple instances of the same type of
application event.
Spring Boot Reference Guide
2.1.0.RELEASE Spring Boot 56
To allow your listener to distinguish between an event for its context and an event for a
descendant context, it should request that its application context is injected and then compare
the injected context with the context of the event. The context can be injected by implementing
ApplicationContextAware or, if the listener is a bean, by using @Autowired.

23.6 web 环境

springapplication 尝试根据你的行为为你创建正确类型的ApplicaitonContext。用于决定是不是web程序的算法十分简单,

TIP
在 JUnit 测试中使用springapplication的时候,使用 setWebApplicationType(WebApplicationType.NONE)

23.7 访问应用参数

通过注入一个org.springframework.boot.ApplicationArguments,可以传给springapplication.run(…)的参数,ApplicationArguments接口提供了原始String[] 参数的访问,也提供了对option和no-option参数的解析

import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
@Component
public class MyBean {
@Autowired
public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }
}

Tip
Spring Boot 也会注册一个包含Spring Enviroment的CommandLinePropertySource,可以使用@value 注入单个应用参数。

23.8 ApplicationRunner 和 CommandLineRunner

如果想在SpringApplication启动之后,执行一些特殊的代码,可以实现applicationRunner或者CommandLineRunner接口。这两个接口工作方式相同,都只提供一个单一的run方法,该方法会在SrpingApplication.run(…)结束前调用。
CommandLineRunner接口以简单string数组的方式访问应用参数,而ApplicaitonRunner则是以上文讲到的ApplicationArguments接口访问应用参数。

import org.springframework.boot.*;
import org.springframework.stereotype.*;
@Component
public class MyBean implements CommandLineRunner {
    public void run(String... args) {
        // Do something...
    }
}

23.9 程序退出

每个SpringAplication都会在jvm中注册一个shutdown hook,以确保ApplicationContext平稳关闭。 所有标准的Spring生命周期回调(比如DisposableBean接口或者\@PreDestroy注解)都会被调用。
此外,当SrpingApplicatin.exit()被调用时,如果想获取特定的exit code,这些beans可以实现org.springframewoek.boot.ExitCodeGenerator接口。System.exit()可以将exit code作为一个status code返回。

@SpringBootApplication
public class ExitCodeApplication {
    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 42;
    }
    public static void main(String[] args) {
        System.exit(SpringApplication
        .exit(SpringApplication.run(ExitCodeApplication.class, args)));
    }
}

并且,异常也可以实现ExitCodeGenerator接口,当遇到该异常时,Spring Boot根据实现的getExitCode()方法,提供exit code

23.10 Admin Features

通过设置spirng.application.admin.enabled启动admin features。

Tip
local.server.port指定应用程序运行的http端口。

24. 外部配置

Spring Boot 允许配置外部化,因为,可以在不同的环境下运行相同的程序。可以使用properties文件、yaml文件、环境变量和命令行参数来外部化配置。属性值可以通过@Value注解注入到bean中,也可以通过@ConfigurationProperties绑定到structured objects中,也可以通过spring Environment abstraction访问。
Spring Boot设计了一个非常特别的PropertySource顺序,以允许对属性值进行合理的覆盖,属性会以如下的顺序进行设值:

  1. 在home目录下配置Devtools全局设置。(~/.spring-boot-devtols.properties,如果devtools激活了)
  2. 测试用例上的@TestProperSource
  3. 测试用例上的@SpringBootTest#properties注解。
  4. 命令行参数
  5. 来自SPRING_APPLICATION_JSON的属性(环境变量或系统属性中内嵌的内联JSON)
  6. ServletConfig初始化参数。
  7. ServletContext初始化参数。
  8. 自于java:comp/env的JNDI属性。
  9. Java系统属性(System.getProperties())。
  10. 操作系统环境变量。
  11. RandomValuePropertySource,只包含random.*中的属性。
  12. 没有打进jar包的Profile-specific应用属性(application-{profile}.properties和YAML变量)。
  13. 打进jar包中的Profile-specific应用属性(application-{profile}.properties和YAML变量)。
  14. 没有打进jar包的应用配置(application.properties和YAML变量)。
  15. 打进jar包中的应用配置(application.properties和YAML变量)。
  16. @Configuration类上的@PropertySource注解。
  17. 默认属性(使用SpringApplication.setDefaultProperties指定)。

假如你正在开发一个有name属性的@Component:

@Component
public class MyBean {
@Value("${name}")
private String name;
    // ...
}

在classpath下,可以使用application.properties,为name提供一个合理的默认值。当在新的环境中运行时,可是使用外置的application.properties覆盖name属性。对于一次性的测试,可以使用不同的命令行启动(比如:java -jar app.jar --name="spring")

Tip
SPRING-APPLICATION-JSON属性可以通过包含环境变量的命令行设置,比如,可以在Unix shell中使用:

    $SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar

在上面的例子中,如果实在Spring Environment中,可以以acme.name=test结尾,如果在一个系统变量中,可以提供作为spring.application.json的JSON字符串:

    $ java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar

或命令行参数:

    $ java -jar myapp.jar --spring.application.json='{"foo":"bar"}'

###说实话,没看懂

Tip  
The SPRING_APPLICATION_JSON properties can be supplied on the command line with an
environment variable. For example, you could use the following line in a UN*X shell:
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
In the preceding example, you end up with acme.name=test in the Spring Environment. You
can also supply the JSON as spring.application.json in a System property, as shown in
the following example:
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
You can also supply the JSON by using a command line argument, as shown in the following
example:
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
You can also supply the JSON as a JNDI variable, as follows: java:comp/env/
spring.application.json.


或作为一个JNDI变量java:comp/env/spring.application.json

24.1 配置随机变量

使用RandomValuePropertySource注入随机变量,提供integers, longs, uuids, or strings。

    my.secret=${random.value}
    my.number=${random.int}
    my.bignumber=${random.long}
    my.uuid=${random.uuid}
    my.number.lees.than.ten=${random.int(10)}
    my.number.in.range=${random.int[1024,65535]}

random.int*语法OPEN value (,max) CLOSEOPEN,CLOSE可以是任何字符,value,max是整数。如果提供了max,则value是最小值,max是最大值(不包含)

24.2 访问命令行属性

默认情况下,SpringApplicaton 将所有的命令行操作参数(那些以–开头的参数,比如server.port=9000)转换成属性添加到Spring Environment中。正如上文所说,来自于命令行的属性优先级高于来自于其他属性源。
可以通过SpringApplication.setAddCommandLineProperties(false)禁用命令行属性。

24.3 Application Property Files

SpringApplication从位于以下位置的application.properties加载属性,并添加到Spring Environment中。

  1. 当前项目根目录的/config子目录
  2. 项目根目录
  3. classpath目录下的/config目录
  4. classpath目录

上面的列表是按照优先级排序的,列表中位置高的路径下定义的属性会覆盖位置的。

Note
可以使用yaml文件替代.properties文件

如果你不喜欢application.properties作为配置文件的名字,可以在环境变量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

Warning
在初期需要根据spring.config.namespring.config.location决定加载哪个文件,所以它们必须定义为environment属性(通常为OS env,系统属性或命令行参数)。 使用命令行,在application中指定没有用

如果spring.config.location中包含了目录(与文件相对应),目录应该以/结尾。在运行时,被加载之前,同spring.config.name关联的名字会被追加到后面,其中就包括profile-specific文件。spring.config.locatioin下定义的文件的使用方法跟其他一样,没有profile-specific变量支持的属性,将被profile-specific的属性覆盖。

按照反序扫描配置文件路径,默认配置的是:classpath:/,classpaht:/config/,file:./,file:./config.所以搜索的顺序是:

  1. file:./config/
  2. file:./
  3. classpath::/config/
  4. classpath:/

如果使用spring.config.location自定义了配置文件位置,替代默认配置,比如,配置了spring.config.location=classpath:custom-config/,file:./custom-config/,搜索的顺序就变成了:

  1. file:./custom-config/
  2. classpath:custom-config/

命令行不能加单引号*

另外,如果使用spring.config.additonal-location自定义配置路径,会在默认配置路径之外添加这些路径,并且,additional locations优先于默认路径扫描。比如,有如下配置spring.config.addtional-location=classpath:/custom-config/,file:./custom-config,扫描顺序如下:

  1. file:./custom-config/
  2. classpath:custom-config/
  3. file:./config/
  4. file:./
  5. classpath:/config/
  6. classpath:/

扫描顺序允许你在一个配置文件中指定默认值,然后有选择的在另外的文件中重写。你可以在application.properties(或者其他文件,需要利用spring.config.name指定)为你的项目指定默认值,这些默认值在运行时可以使用不同的文件覆盖。

Note
如果使用环境变量而不是系统属性,需要注意多数操作系统的key名称不允许以句号分割(period-separated),但你可以使用下划线(underscores)代替(比如,使用SPRING_CONFIG_NAME代替spring.config.name

24.4 Profile-specific Properties

profile-specific属性通过命名惯例application-{profile}.properties定义。如果没有配置激活的profile,Environment有一系列默认值(默认为[default]),换句话说,如果没有明确激活一个profiles,就会从application-default.properties中加载属性。
Profile-specific属性加载路径和标准的application.properties相同,并且profile-specific文件总是会覆盖non-specific文件,不管profile-specific文件是否被打包到jar中。

如果定义多个profiles,最后一个将获胜。例如,spring.profiles.active定义的profiles被添加到通过SpringApplicationAPI定义的profiles后面,因此优先级更高。

Note
如果你已经在spring.config.location下定义所有文件(非目录),那些profile-specific的文件将不被考虑。如果想使用profile-specific属性,那就在spring.config.location下使用目录

24.5 属性占位符

当使用application.properties中的属性时,Spring 会先搜寻已经存在的Environment,所以你可以引用事先定义的值。(比如,系统属性)

    app.name=MyApp
    app.description=${app.name} is a Spring Boot application

Tip
可以为存在的spring boot 属性创建缩写。具体参考后面。

24.6 加密属性值

Spring Boot 并没有内嵌对属性值加密的支持。但是它提供了修改spring environment中包含的属性值,所必需的hook points。在程序启动之前可以利用EnvironmentPostProcessor接口修改Environment。参考 76.3 Customize the Environment or ApplicationContext Before It Starts.
如果你正在寻找一种安全的方式来存储凭证和密码,Spring Cloud Vault项目支持在HashiCorp Vault中存储外部化配置。

24.7 使用YAML代替Properties

YAML是JSON的超集,也是一种方便定义层次配置数据的格式。只要将SnakeYAML库放到classpath下面,SpringApplication就会自动支持YAML,替代properties.

Note
如果使用“Starters",spirng-boot-starter自动包含SnakeYAML

Loadinng YAML

Spring Framework提供了两种方便的类用于加载YAML文档。YamlPropertiesFactoryBean以properties的方式加载YAML,而YamlMapFactoryBean以map的方式加载YAML。
比如,我们有如下的YAML文档:

    environment:
        dev:
            url: http://dev.example.com
            name: Developer Setup 

下面是转化成properties的样子:

    environments.dev.url=http://dev.example.com
    environments.dev.name=Developer Setup
    environments.prod.url=http://another.example.com
    environments.prod.name=My Cool App

YAML 列表 的格式是属性键和带有[index]间接引用。例如下面的YAML:

my:
    server:
        - dev.example.com
        - another.example.com

对应的properties文件则是:

    my.servers[0]=dev.example.com
    my.servers[1]=another.example.com

使用 Spring Boot的Binder 工具集(这也就是@ConfigurationProperties所做的事)绑定这些属性的时候,需要确保目标bean有一个java.util.List Or(set),并且需要提供Setter方法或者使用一个可变的值初始化它。比如下面的代码绑定上述的属性。

    @ConfigurationProperties(prefix="my")
    public class Config {
            private List<String> servers = new ArrayList<String>();
            public List<String> getServers() {
            return this.servers;
        }
    }
Exposing YAML as Properties in the Spring Environment

YamlPropertySourceLoader类可以将YAML作为PropertySource导出到Sprig Environment,这允许你使用常用的@Value注解配合占位符语法访问YAML属性。

Multi-profile YAML Documents

可以在单个文件中指定多个 profile-specific 的YAML文档,使用spring.profies指定当前文档块所属的profiles。

server:
    address: 192.168.1.100
---
spring:
    profies: development
server:
    address: 127.0.0.1
---
spring:
    profies: production & eu-central
server:
    address: 192.168.1.120

上面的例子,如果激活了development profie,那么server.address属性值就是127.0.0.1,如果激活了production & eu-central profile,那么server.address属性值就是192.168.1.120,如果没有指定激活那个,默认就是最上面的192.168.1.100

Note
spring.profiles的值可以是一个简单的profile,或者是一个profile expressionprofile.expression允许更为复杂的profile逻辑,比如:

!: A logical “not” of the profile

&: A logical “and” of the profiles

`|`_ A logical “or” of the profiles

程序上下文启动时如果没有明确的激活一个profiles,则激活默认的profiles.在下面的例子中,我们为spring.security.user.passsword设置了一个值,但是该值只能在"default"profile中使用。

server:
    port: 8080
---
spring::
    profiles : default
    security: 
        user:
            passsword: week

但是,下面的例子,password设置的值总会起作用,因为并没有指定任何一个profile,并且,如果有必要的话,会在其它profiles中显示重置。

    server:
        port: 8080
        spring :
            security:
                user:
                    passsword: week

没看懂

通过!可以对spring.profiles指定的profiles进行取反(negated,跟java中的!作用一样),如果negated和non-negated profiles都指定一个单一文件,至少需要匹配一个non-negated profile,可能不会匹配任何negated profiles。
Spring profiles designated by using the spring.profiles element may optionally be negated by using the ! character. If both negated and non-negated profiles are specified for a single document, at least one non-negated profile must match, and no negated profiles may match.

YAML的缺点

YAML文件不能使用\@PropertySource注解方式加载。如果需要使用该方式,那就必须使用properties文件。

24.8 类型安全的配置属性(这块没看懂,还不如看视频,讲的清楚)

使用\@Value("${property}")注解注入配置属性的方式有时会很麻烦,尤其是如果你配置了很多属性,或者你的数据本质上是分层的。Spring Boot提供另外一种使用属性的方式,可以管理strongly typed beans和验证程序的配置,如下所述:

package com.example.demo.Component;

import lombok.Getter;
import lombok.Setter;

import java.net.InetAddress;
import java.util.ArrayList;Maps, as long as they are initialized, need a getter but not necessarily a setter, since they can be mutated by the binder
import java.util.Collections;
import java.util.List;

@Setter
@Getter
public class AcmeProperties {
    private boolean enabled;
    private InetAddress inetAddress;
    private final Security security = new Security();

    @Getter
    @Setter
    public static class Security {
        private String name;
        private String password;
        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

    }
}

前面的POJO定义了如下属性:

  • acme.enabled ,默认值是false
  • acme.remote-address,可以从字符串转换成该类型
  • acme.security.username ,
  • acme.security.password
  • acme.security.roles

Note
Getters 和 Setters通常是可选的。
属性绑定只考虑标准java bean 的属性,不支持绑定静态属性。

实现绑定还需要在\@EnableConfigurationProperties中注册属性类,如下所示:

    @Configuration
    @EnableConfigurationProperties(AcmeProperties.class)
    public class MyConfiguration{

    }

Note
使用\@ConfigurationProperties注解之后,bean有一个传统的名字:<prefix>-<fqn>,其中,<prefix>是在\@ConfigurationProperties注解中指定的environment key 前缀,<fqn>是bean的全限定名。比如,上面定义的bean的名字就是acme-com.example.AcmeProperties

Even if the preceding configuration creates a regular bean for AcmeProperties, we recommend
that @ConfigurationProperties only deal with the environment and, in particular, does not
inject other beans from the context. Having said that, the @EnableConfigurationProperties
annotation is also automatically applied to your project so that any existing bean annotated
with @ConfigurationProperties is configured from the Environment. You could shortcut
MyConfiguration by making sure AcmeProperties is already a bean, as shown in the following
example:
@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
// ... see the preceding example
}
This style of configuration works particularly well with the SpringApplication external YAML
configuration, as shown in the following example:
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
To work with @ConfigurationProperties beans, you can inject them in the same way as any other
bean, as shown in the following example:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
Tip
Using @ConfigurationProperties also lets you generate metadata files that can be used by
IDEs to offer auto-completion for your own keys. See the Appendix B, Configuration Metadata
appendix for details.
第三方配置

@ConfigurationProperties不仅可以注解在类上,也可以注解在public @Bean方法上,当你需要为不受控的第三方组件绑定属性时,该方法将非常有用。
为了从Environment属性中配置一个bean,需要在该bean注册上使用\@ConfigurationProperties注解

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
    ...
}

和上面的AcmeProperties一样,任何以another开头的属性都会被映射到AnotherComponent bean中。

Relaxed绑定

Spring Boot将Environment属性绑定到@ConfigurationProperties beans时会使用一些宽松的规则,所以Environment属性名和bean属性名不需要精确匹配。常用的例子包括虚线分割(比如,context-path绑定到contextPath)和环境属性大小写转换。(比如 PORT绑定到port)
考虑下面的例子

@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大写形式,使用系统环境变量时推荐

注意
\@ConfigurationProperties注解的prefix值必须是虚线表示,(小写并且是虚线分割,比如acme.my-project.person)

下表是 不同属性源的宽松绑定规则:

Property SourceSimpleList
Properties驼峰、虚线或者下划线标准[] list语法,或者逗号分割
YAML驼峰、虚线或者下划线标准的YMAL list 语法(-)或者逗号分隔
环境变量用下划线分割的大写字母,_不能用于一个属性名的内部下划线包围的数字,比如MY_ACME_1_OTHER=…
System Properties驼峰、虚线或者下划线标准[] list语法,或者逗号分割

TIP
建议使用小写虚线的格式,比如my.property-name=acme

当绑定属性到map时,如果key中包含了除 小写字母、数字或者-时,需要用括号保留原始值,如果没有被[]括起来,除了字母数字下划线的字符都会被移除。比如:

    acme:
         map:
            "[/key1]": value1
            "[/key2]": value2
            /key3: value3

上面的配置,绑定到map中的key有 /key1,/key2和key3。

合并复杂类型

如果列表配置在不止一个地方,会将整个列表的内容重写。
比如,假设有一个MyPojo的对象,对象中有name 和description两个属性,默认是null。下面的例子从AcmeProperties暴露出了一个MyPojo的list。

@ConfigurationProperties("acme")
public class AcmeProperties {

    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }

}

考虑下面的配置:

acme:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

如果dev profile没有被激活,AcmeProperties.list只包含一个MyPojo entity。如果dev profile被激活,AcmeProperties.list 仍然只有一个 entity(名字是 my another name ,description是null)。spring boot 配置并不往list中添加第二个MyPoJo 实例,不做合并。

当在多个 profiles中配置一个list的时候,会使用具有最高优先级的那个,比如:

acme:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

在上面的列子中,如果激活了dev profile,AcmeProperties.list 中包含一个 MyPojo entity(name是 my another name,description是null)。对于YAML来说,逗号分割的list和YAML的标准list都可以用来重写list的所有contents。

对于map属性,可以绑定多个来源的属性,但是,对于不同来源相同属性,会使用具有最高优先级的。比如:

@ConfigurationProperties("acme")
public class AcmeProperties {

    private final Map<String, MyPojo> map = new HashMap<>();

    public Map<String, MyPojo> getMap() {
        return this.map;
    }

}

考虑下面的配置:

acme:
  map:
    key1:
      name: my name 1
      description: my description 1
---
spring:
  profiles: dev
acme:
  map:
    key1:
      name: dev name 1
    key2:
      name: dev name 2
      description: dev description 2

如果没有激活dev profile,AcmeProperties.map 会包含一个键(name是 my name 1,description 是 my description 1)。如果激活了dev profile ,AcmeProperties.map会包含两个键,*key1(name是 dev name 1,description 是 my description 1)*和key2(name 是 dev name 2,description是 dev description 2)

上面说的合并规则,适用所有属性源,不仅仅是yaml文件

属性转换

将外部应用绑定到\@ConfigurationProperties beans时,spring boot 会强制将属性转换为正确的类型。如果需要自定义转换,可以提供一个ConversionServicebaan(bean name 为conversionService),或者通过一个CustomEditorConfigurer自定义属性编辑器,或者自定义Converters(bean定义时需要注解@ConfigurationPropertiesBinding)

Note
因为ConversionService bean在应用程序生命周期的早期就需要使用,所以需要再三限制ConversionService使用到的依赖,通常来说,在创建的时候任何你需要的以来可能都没有被完全初始化。

时间转换

Spring boot 专门支持时间表达式。如果配置了一个java.time.Duration类型的属性,application properties支持下面的格式:

  • 一个long 值(使用毫秒作为默认单位,也可以通过`@DurationUnit指定)
  • java.util.Duration使用的ISO-8601标准格式
  • 更加可读的格式:值和单位共同给出(比如10s,意味着10secones)

考虑下面的例子

@ConfigurationProperties("app.system")
public class AppSystemProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    private Duration readTimeout = Duration.ofMillis(1000);

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public void setSessionTimeout(Duration sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

    public void setReadTimeout(Duration readTimeout) {
        this.readTimeout = readTimeout;
    }

}

指定sessiontimeout 为30 秒时,30,PT30S,30s是等价的。
使用500,PT0.5s或者500ms可以将readtimeout指定为500ms。

  • ns for nanosecondsif it isn’t milliseconds alongside the switch to Duration
  • us for microseconds
  • ms for milliseconds
  • s for seconds
  • m for minutes
  • h for hours
  • d for days

默认的单位是毫秒,可以使用@DurationUnit重写。

TIP
如果你正在从一个只是通过long表达duration的版本升级,,你需要确认使用\@DurationUnit定义单位,

Converting Data Sizes

Spring Framework 有个DataSize 类型,可以展示占用多少字节。如果你设置DataSize属性,applicaton properties支持下面的格式: 、

  • 一个long值,(默认单位是bytes,使用@DurationUnit重写)
  • 更加可读的格式,同时给出值和单位(比如10MB)
@ConfigurationProperties("app.io")
public class AppIoProperties {

    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);

    private DataSize sizeThreshold = DataSize.ofBytes(512);

    public DataSize getBufferSize() {
        return this.bufferSize;
    }

    public void setBufferSize(DataSize bufferSize) {
        this.bufferSize = bufferSize;
    }

    public DataSize getSizeThreshold() {
        return this.sizeThreshold;
    }

    public void setSizeThreshold(DataSize sizeThreshold) {
        this.sizeThreshold = sizeThreshold;
    }

}

可是使用10或者10MB指定10 兆,256字节的大小阈值可以指定为256或256 b。

  • B for bytes
  • KB for kilobytes
  • GB for gigabytes
  • TB for terabytes

默认的单位是bytes,可是使用\@DataSizeUnit重写。

\@ConfigurationProperties Validation

spring boot 会校验同时使用\@ConfigurationProperties\@Validate的类。你可以直接将JSR-303 java.validation约束注解 应用到ConfigurationProperties类上。
首先确保在你的classpath下有兼容的JSR303实现,然后将约束注解添加到相应的字段上。

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

TIP
You can also trigger validation by annotating the @Bean method that creates the configuration
properties with @Validated.

尽管嵌套的属性在绑定的时候会被校验,但是仍然建议在级联字段上使用\@Valid注解,确保能够触发校验。

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}

你也可以添加自定义的 Spring validator,需要创建一个configurationPropertiesValidator,而且\@Bean注解的方法应该声明为static。因为Configuration properties validator 在程序生命周期的早期就被创建,所以将\@Bean方式声明为static可以创建该bean而不用实例化\@Configuration类。这样避免了过早实例化带来的问题。

@ConfigurationProperties vs. @Value

\@Value注解是容器的一个核心特性,并没有提供tye-safe configuration相同的特性。下表是\@ConfigurationProperties\@Value支持的特性。

Feature\@Configuration\@Value
Relaxed bindingYesNo
Meta-data SupportYesNo
SpEL evaluationNoYes

如果你为自己的组件定义了一系列的配置keys,我们建议你将它们以@ConfigurationProperties注解的POJO进行分组。由于@Value不支持relaxed绑定,所以如果你使用环境变量提供属性值的话,它就不是很好的选择。最后,尽管@Value可以写SpEL表达式,但这些表达式不会处理来自Application属性文件的属性。

25 Profiles

spring profiles 提供了一种隔离应用程序配置的方式,并使这些配置只在指定的环境中生效。所有的\@Component\@Configuration都可以使用\@Profile注解限制加载的时机。

@Configuration
@Profile("production")
public class ProductionConfiguration {

    // ...

}

可以使用spring.profiles.active环境属性指定激活哪个profile。

25.1 添加激活的profiles

spring.profiles.active和其他属性一样遵循优先级规则。具有最高优先级的propertySource获胜。这意味着你可以在application.properties激活任意的profiles,然后通过命令行切换。

有时,将profile-specific的属性添加到激活的配置中而不是直接替换它们是有好处的。spring.profiles.include可以用于无条件的添加激活的属性.SpringApplication入口也有一个java api 用于设置附加的profiles,通过其激活的属性优先级比spring.profiles.active激活的属性要高。具体参考SpringApplication 的 setAddtionalProfiles()方法。
比如,当程序有下面的属性,并使用--spring.profiles.active=prod,proddb和prodmq也会被激活。

---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include:
  - proddb
  - prodmq

25.2 以编程的方式设置profiles

可以通过调用SpringApplication.setAddtionalProfiles(…)以编程的方式设置激活的profiles。也可以使用spring的ConfigurableEnvironment接口激活profiles。、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值