1、SpringApplication
SpringApplication类提供了一种方便的方法来引导从main()方法启动的Spring应用程序。在很多情况下,你可以委托给静态的SpringApplication.run方法,如下面的例子所示:
public static void main(String[] args) { SpringApplication.run(MySpringConfiguration.class, args); } 当运行main方法之后控制台输出内容: . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: v2.1.5.RELEASE 2013-07-31 00:08:16.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb) 2013-07-31 00:08:16.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy 2014-03-04 13:09:54.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080 2014-03-04 13:09:56.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)默认是info级别的日志,如果需要设置日志级别可以参考这篇文章
1.1)启动失败 Spring Boot提供了许多FailureAnalyzer实现,您可以添加自己的实现。参考这篇文章 假设现在端口冲突一般你会看到如下错误提示信息:
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.如果没有失败分析器能处理异常,那么可以启用debug属性或启用debug日志记录 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener 还有一种开启方式是通过使用如下命令:
java -jar myproject-0.0.1-SNAPSHOT.jar --debug1.2)定制自己的启动banner图 可以通过在类路径中添加一个banner.txt文件或设置spring.banner.location来更改启动的时候控制台打印的banner。属性指向此类文件的位置。如果文件的编码不是UTF-8,可以设置spring.banner.charset。除了文本文件,您还可以添加一个横幅。将gif、banner.jpg或banner.png图像文件保存到类路径或spring.banner.image.location属性。图像被转换成ASCII艺术表示形式,并打印在任何文本banner之上。 在banner.txt文件中,可以使用以下任何占位符
变量描述${application.version}MANIFEST.MF中声明应用的版本号,比如1.0${application.formatted-version}MANIFEST.MF中声明应用的版本号加V比如v1.0${spring-boot.version}使用的SpringBoot的版本号比如这里2.1.5.RELEASE${spring-boot.formatted-version}使用的SpringBoot的版本号比如这里2.X前面加个V,v2.1.5.RELEASE${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME})其中NAME是ANSI转义码的名称。有关详细信息,请参见AnsiPropertySource${application.title}MANIFEST.MF中声明的标题,比如Implementation-Title: MyApp则会打印MyApp。【提示】 如果希望以编程方式生成横幅,可以使用SpringApplication.setBanner(…)方法。使用org.springframework.boot.Banner接口并定制自己的printBanner()方法。
如果不希望输出,则你可以通过spring.main.banner-mode属性来控制(yaml文件设置下属性,参考下图),实际上他的原理实现是一个单例bean(springBootBanner)
1.3)定制SpringApplication 如果不喜欢SpringBoot应用功能的默认值可以实现类的形式关掉,比如banner图。 传递给SpringApplication的构造函数参数是Spring bean的配置源。在大多数情况下,这些是对@Configuration类的引用,但也可以是对XML配置或应该扫描的包的引用。 当然在application.properties文件中也可以配置,可以参考下面的【2.0、外部化配置】,配置选项可以参考java官网文档
1.4)流利的构建器API 如果需要构建上下文继承关系,或者想要构建出配置的层次关系可以使用SpringApplicationBuilder 举个例子: 创建ApplicationContext层次结构时存在一些限制。例如,Web组件必须包含在子上下文中,父上下文中使用相同的环境。具体的支持参数可以参考这篇文章
1.5)应用程序事件和监听器 除了通常的Spring框架事件(如ContextRefreshedEvent)之外,Spring应用程序还发送一些附加的应用程序事件 在创建应用上下文ApplicationContext之前,你不能通过Bean注册监听器,但是可以通过 SpringApplication.addListeners(…)或者SpringApplicationBuilder.listeners(…)来添加监听器。如果想要监听器自动注册可以添加一个文件(META-INF/spring.factories),通过使用org.springframework.context.ApplicationListener作为key来添加,举个例子如下:
org.springframework.context.ApplicationListener=com.example.project.MyListener启动应用的时候应用事件发送的顺序如下:
除了监听器的注册和初始化,ApplicationStartingEvent是第一个。 当要在上下文中使用的环境已知但在创建上下文中之前发送ApplicationEnvironmentPreparedEvent ApplicationPreparedEvent在启动刷新之前发送,但在加载bean定义完成之后 在刷新上下文之后,但在调用任何应用程序和命令行运行程序之前,将发送一个ApplicationStartedEvent 在调用任何应用程序和命令行运行程序之后,都会发送一个ApplicationReadyEvent。它表明应用程序已经准备好为请求提供服务 如果启动时出现异常,则发送ApplicationFailedEvent当然一般的时候我们不需要自己写监听。了解上面的意义是因为SpringBoot依赖很多监听机制完成的,这样有助于深入了解SpringBoot。 应用程序事件是通过使用Spring Framework的事件发布机制发送的。此机制的一部分确保在子上下文中发布给侦听器的事件也在任何祖先上下文中发布给侦听器。因此,如果应用程序使用SpringApplication实例的层次结构,监听器可能会接收同一类型应用程序事件的多个实例。为了让监听器能够区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现applicationcontext ware注入,如果监听器是bean,则可以通过使用@Autowired注入。
1.6)web环境 SpringApplication会自动创建正确的应用上下文类型,用于确定WebApplicationType的算法相当简单
如果存在Spring MVC,则使用AnnotationConfigServletWebServerApplicationContext 如果Spring MVC不存在,而Spring WebFlux存在,则使用AnnotationConfigReactiveWebServerApplicationContext 否则,将使用AnnotationConfigApplicationContext。 从上面的算法策略来看,如果你使用spring mvc和spring webFlux的new webCLient在相同的应用里面。springMVC会被默认使用,你可以通过调用setWebApplicationType(WebApplicationType)来重写。当然也可以通过setApplicationContextClass(…)来完全控制应用上下文ApplicationContext的类型 在JUnit测试中使用SpringApplication时,通常需要调用setWebApplicationType(WebApplicationType.NONE)1.7)访问应用参数 如果你想要知道应用参数传递的是什么?可以通过注入org.springframework.boot.ApplicationArguments这个bean。 ApplicationArguments接口提供了访问原生String[]参数和被解析后的选项和非选项的参数。 可以参考如下例子:
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"] } } Spring Boot还向Spring环境注册了一个CommandLinePropertySource。 允许通过使用@Value注释注入单个应用程序参数1.8)使用ApplicationRunner或CommandLineRunner SpringApplication应用启动,你想要运行一些指定的代码,那么可以通过实现ApplicationRunner 或者 CommandLineRunner接口,两个接口工作方式相同,并都提供了一个run方法,但是必须是在SpringApplication.run(…)完成之前调用 CommandLineRunner接口以简单的字符串数组的形式提供对应用程序参数的访问,而ApplicationRunner使用前面讨论的ApplicationArguments接口。下面的例子显示了一个带有run方法的CommandLineRunner
import org.springframework.boot.*; import org.springframework.stereotype.*; @Component public class MyBean implements CommandLineRunner { public void run(String... args) { // Do something... } }如果定义了必须以特定顺序调用的CommandLineRunner或ApplicationRunner bean,则可以另外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注释
1.9)应用退出 每个SpringApplication都在JVM注册了关闭钩子来确保应用上下文在退出的时候可以优雅的关闭。可以使用所有标准的Spring生命周期回调(例如DisposableBean接口或@PreDestroy注释)。 另外bean可能需要实现org.springframework.boot.ExitCodeGenerator,如果当SpringApplication.exit()方法被调用,然后需要返回个特定的异常码。然后可以将此退出代码传递给System.exit(),将其作为状态代码返回,如下面的示例所示:
@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()方法提供的退出代码1.10)管理员特征 可以通过指定spring.application.admin为应用程序启用与管理相关的特性。启用属性。这将在MBeanServer平台上公开SpringApplicationAdminMXBean。您可以使用此功能远程管理Spring引导应用程序。该特性还可以用于任何服务包装器实现。 如果您想知道应用程序在哪个HTTP端口上运行,请获取带有local.server.port键的属性 启用此功能时要小心,因为MBean公开了一个方法来关闭应用程序
2.0、外部化配置
SpringBoot为了在不同环境中使用相同的代码可以通过属性文件properties ,yaml文件,环境变量或者命令行来外部化配置。属性值也可以通过使用@Value直接注入beans中,通过Spring的环境抽象访问,或者通过@ConfigurationProperties绑定到结构化对象(可参考下面的类型安全的配置属性) Spring Boot使用一种非常特殊的PropertySource顺序,其设计目的是允许合理地覆盖值。属性按以下顺序考虑
开发工具可以参考昨天的文章:@TestPropertySource注释测试属性。可以在@SpringBootTest和测试注释中找到,用于测试应用程序的特定部分命令行参数来自SPRING_APPLICATION_JSON的属性(嵌入到环境变量或系统属性中的内联JSON)ServletConfig初始参数ServletContext初始参数java:comp/env中的JNDI属性java系统属性System.getProperties()操作系统环境变量个只有random.*属性的RandomValuePropertySource打包jar (application-{profile}.properties)内外部的特定应用程序属性。属性和YAML变体)—参考如下的属性特定文件的指定。打包jar(application.properties)内外部的应用程序属性。属性和YAML变体)—参考如下的属性特定文件的指定。@Configuration类上的@PropertySource注释。默认属性(通过设置SpringApplication.setDefaultProperties指定) 假设有个例子开发一个bean需要使用配置文件中的name属性 import org.springframework.stereotype.*; import org.springframework.beans.factory.annotation.*; @Component public class MyBean { @Value("${name}") private String name; // ... }在应用中也可以启动的时候指定不同的配置类,假设你的配置类有uat/pre/prod测试,预发,生产。
2.1)配置Random值 RandomValuePropertySource用于注入随机值(例如,注入秘密或测试用例)。它可以生成整数、long、uuid或字符串,如下面的示例所示 random.int*语法是OPEN value (,max) CLOSE,其中OPEN、CLOSE是任意字符和值,max是整数。如果提供max,则value为最小值,max为最大值(排除)
2.2)访问命令行属性 默认情况下,SpringApplication将任何命令行选项参数(即以——例如——server.port=9000开头的参数)转换为属性,并将它们添加到Spring环境中。如前所述,命令行属性始终优先于其他属性源。 如果不希望将命令行属性添加到环境中,可以使用SpringApplication.setAddCommandLineProperties(false)禁用它们
2.3)应用属性文件 application.properties/application.yml作为spring配置环境的加载类。 存放位置:
当前目录的一个子目录 当前目录 类路径/配置包 根路径列表按优先级排序(在列表中较高位置定义的属性覆盖在较低位置定义的属性 如果不喜欢以上述这样命名那么可以指定配置文件改为你喜欢的名字:
比如你的配置文件名称为myproject.yml java -jar myproject.jar --spring.config.name=myproject 假设你有多个配置文件需要指定 java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties配置位置以相反的顺序搜索。默认情况下, 当使用spring.config配置自定义配置位置时。它们替换默认位置。例如,if spring.config。location使用值classpath:/custom-config/,file:配置。/custom-config/,搜索顺序如下 当使用spring.config配置自定义配置位置时。附加位置,它们是在默认位置之外使用的。在默认位置之前搜索其他位置。例如,如果类路径:/custom-config/、file:的附加位置。/custom-config/已配置,搜索顺序如下 这种搜索顺序允许您在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。您可以在应用程序中为应用程序提供默认值。属性(或您在spring.config.name中选择的任何其他基本名称)位于一个默认位置。然后,可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。如果使用环境变量而不是系统属性,大多数操作系统不允许使用周期分隔的键名,但是可以使用下划线(例如,SPRING_CONFIG_NAME而不是spring.config.name) 如果您的应用程序在容器中运行,则可以使用JNDI属性(在java:comp/env中)或servlet上下文初始化参数来代替环境变量或系统属性,或者同时使用环境变量或系统属性。
2.4)application.properties也可以使用如下命名规范来定义application-{profile}.properties,但是如果你没有指定激活的文件依然走的是默认文件加载 application-default.properties 如果指定了多个配置文件,则应用“最后胜出”策略。例如,spring.profiles.active指定的概要文件。活动属性是在通过SpringApplication API配置的属性之后添加的,因此具有优先级。还可以通过spring.config.location来指定配置文件的路径。
2.5)占位符的属性 应用程序中的值。属性在使用时通过现有环境进行筛选,因此您可以引用以前定义的值(例如,来自系统属性)更多的可以参考这篇文章
app.name=MyApp app.description=${app.name} is a Spring Boot application2.6)加密属性 Spring Boot不提供任何内置的对属性值加密的支持,但是它提供了修改Spring环境中包含的值所必需的挂钩点.EnvironmentPostProcessor接口允许您在应用程序启动之前操作环境,可以参考这篇文章 如果您正在寻找一种安全的方式来存储凭证和密码,Spring Cloud Vault项目提供了在HashiCorp Vault中存储外部化配置的支持
2.7)使用yml替代properties文件 YAML是JSON的一个超集,因此是指定分层配置数据的一种方便的格式。当您的类路径上有SnakeYAML库时,SpringApplication类自动支持YAML作为属性的替代 如果项目中使用“starter”,SnakeYAML将由spring-boot-starter自动提供
2.7.1)Spring Framework提供了两个方便的类,可用于加载YAML文档。YamlPropertiesFactoryBean将YAML加载为属性,YamlMapFactoryBean将YAML加载为映射 举个例子:
environments: dev: url: https://dev.example.com name: Developer Setup prod: url: https://another.example.com name: My Cool App 上述内容yml文件会转化为key-value状态 environments.dev.url=https://dev.example.com environments.dev.name=Developer Setup environments.prod.url=https://another.example.com environments.prod.name=My Cool App YAML列表用[索引]解引用器表示为属性键。例如,考虑以下YAML my: servers: - dev.example.com - another.example.com 上述内容yml会转为 my.servers[0]=dev.example.com my.servers[1]=another.example.com 要使用Spring Boot的绑定器实用程序 (这就是@ConfigurationProperties所做的)绑定到这样的属性,您需要在java.util类型的目标bean中有一个属性。 列表(或集合),您要么需要提供setter,要么使用可变值初始化它。 例如,下面的示例绑定到前面显示的属性 @ConfigurationProperties(prefix="my") public class Config { private List<String> servers = new ArrayList<String>(); public List<String> getServers() { return this.servers; } }2.7.2)在Spring环境中将YAML作为属性公开 YamlPropertySourceLoader类可用于在Spring环境中将YAML公开为PropertySource。这样做可以使用@Value注释和占位符语法访问YAML属性 2.7.3)您可以使用spring在一个文件中指定多个特定于概要文件的YAML文档。配置文件键,指示文档何时应用,如下例所示
server: address: 192.168.1.100 --- spring: profiles: development server: address: 127.0.0.1 --- spring: profiles: production & eu-central server: address: 192.168.1.120在前面的示例中,如果开发概要文件处于活动状态,则服务器。地址属性是127.0.0.1。类似地,如果生产和eu-central概要文件处于活动状态,则服务器也处于活动状态。地址属性是192.168.1.120。如果未启用开发、生产和eu-central概要文件,则该属性的值为192.168.1.100 因此spring.profiles概要文件可以包含简单的概要文件名称(例如产品)或概要文件表达式。概要文件表达式允许表达更复杂的概要文件逻辑,例如production & (eu-central | eu-west)。查看参考指南了解更多细节 如果在应用程序上下文启动时没有显式活动,则激活默认概要文件。因此,在接下来的YAML中,我们为spring.security.user设置了一个值。只有在“默认”配置文件中可用的密码
server: port: 8000 --- spring: profiles: default security: user: password: weak 在下面的例子中,密码总是被设置, 因为它没有附加到任何配置文件中, 而且它必须在所有其他配置文件中根据需要显式重置: server: port: 8000 spring: security: user: password: weak 使用!字符可以将spring.profiles中的元素置为无效。如果为单个文档指定了否定的和非否定的概要文件,则必须至少匹配一个非否定的概要文件,并且不能匹配任何否定的概要文件。2.7.4)YAML的缺点 无法使用@PropertySource注释加载YAML文件。因此,在需要以这种方式加载值的情况下,需要使用属性文件 在特定于概要文件的YAML文件中使用多YAML文档语法可能导致意外行为。例如,在一个名为application-dev的文件中考虑以下配置。yml,其中dev配置文件处于活动状态
server: port: 8000 --- spring: profiles: !test security: user: password: weak 在上面的示例中,概要文件否定和概要文件表达式的行为与预期不同。我们建议您不要组合特定于概要文件的YAML文件和多个YAML文档,坚持只使用其中一个2.8)类型安全的配置属性 使用@Value(“${property}”)注释注入配置属性有时会很麻烦,特别是当您处理多个属性或者您的数据本质上是分层的时候。Spring Boot提供了另一种处理属性的方法,该方法允许强类型bean管理和验证应用程序的配置,如下面的示例所示
package com.example; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("acme") public class AcmeProperties { private boolean enabled; private InetAddress remoteAddress; private final Security security = new Security(); public boolean isEnabled() { ... } public void setEnabled(boolean enabled) { ... } public InetAddress getRemoteAddress() { ... } public void setRemoteAddress(InetAddress remoteAddress) { ... } public Security getSecurity() { ... } public static class Security { private String username; private String password; private List<String> roles = new ArrayList<>(Collections.singleton("USER")); public String getUsername() { ... } public void setUsername(String username) { ... } public String getPassword() { ... } public void setPassword(String password) { ... } public List<String> getRoles() { ... } public void setRoles(List<String> roles) { ... } } } 前面的POJO定义了以下属性: acme.enabled默认false acme.remote-address从String强制转换 acme.security.username使用嵌套的“security”对象,该对象的名称由属性的名称决定。特别是,返回类型根本不使用,可能是SecurityProperties acme.security.password acme.security.roles使用字符串集合getter和setter通常是强制性的,因为绑定是通过标准的Java bean属性描述符进行的,就像Spring MVC一样。在下列情况下,可以省略setter
只要初始化映射,就需要getter,但不一定需要setter,因为绑定器可以对映射进行修改 集合和数组可以通过索引(通常使用YAML)或使用单个逗号分隔的值(属性)访问。在后一种情况下,setter是强制性的。我们建议始终为此类类型添加setter。如果初始化一个集合,请确保它不是不可变的(如前面的示例所示) 如果初始化嵌套POJO属性(如前面示例中的Security字段),则不需要setter。如果希望活页夹使用其默认构造函数动态创建实例,则需要一个setter。有些人使用Project Lombok自动添加getter和setter。确保Lombok不会为这样的类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象 最后,只考虑标准Java Bean属性,不支持绑定静态属性 可以看下面的@Value 和@ConfigurationProperties.区别
上面的pojo类声明以后需要将其声明为配置类:
@Configuration @EnableConfigurationProperties(AcmeProperties.class) public class MyConfiguration { } 当以这种方式注册@ConfigurationProperties bean时, bean有一个常规名称:<prefix>-<fqn>, 其中<prefix>是@ConfigurationProperties注释中指定的环境键前缀, <fqn>是bean的完全限定名。 如果注释不提供任何前缀,则只使用bean的完全限定名 上面示例中的bean名称是acme-com.example.AcmeProperties。前面的配置为AcmeProperties创建了一个常规bean。我们建议@ConfigurationProperties只处理环境,特别是不从上下文注入其他bean。请记住,@EnableConfigurationProperties注释也会自动应用到您的项目中,以便使用@ConfigurationProperties注释的任何现有bean都是从环境中配置的。不需要使用@EnableConfigurationProperties(AcmeProperties.class)注释MyConfiguration,您可以将AcmeProperties作为bean,如下面的示例所示
@Component @ConfigurationProperties(prefix="acme") public class AcmeProperties { // ... see the preceding example } 这种类型的配置对于SpringApplication外部YAML配置尤其有效, 如下面的示例所示: # application.yml acme: remote-address: 192.168.1.1 security: username: admin roles: - USER - ADMIN # additional configuration as required 要使用@ConfigurationProperties bean, 可以以与任何其他bean相同的方式注入它们, 如下面的示例所示: @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()); // ... } }使用@ConfigurationProperties还可以生成元数据文件, ide可以使用这些元数据文件为您自己的键提供自动完成功能
2.8.1)第三方配置 除了使用@ConfigurationProperties注释类外,还可以在公共@Bean方法上使用该类。当您希望将属性绑定到您无法控制的第三方组件时,可以如下这样做 要从环境属性配置bean,请将@ConfigurationProperties添加到其bean注册中,如下面的示例所示
@ConfigurationProperties(prefix = "another") @Bean public AnotherComponent anotherComponent() { ... } 使用另一个前缀定义的任何属性都映射到另一个组件bean, 其方式类似于前面的AcmeProperties示例2.8.2)不精确的绑定 Spring Boot使用一些轻松的规则将环境属性绑定到@ConfigurationProperties bean,因此环境属性名和bean属性名之间不需要精确匹配。有用的常见示例包括与dash分离的环境属性(例如,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; } }属性的格式和要求:
属性备注acme.my-project.person.first-namekebab的例子,推荐使用.properties文件或者.yml文件acme.myProject.person.firstName标准的camel语法acme.my_project.person.first_name下划线表示法,这是.properties和.yml文件中使用的另一种格式ACME_MYPROJECT_PERSON_FIRSTNAME大写格式,在使用系统环境变量时推荐使用注释的前缀值必须是kebab大小写 (小写,并由-分隔,如acme.my-project.person)
放宽每个属性源的绑定规则
属性源简述清单属性文件驼峰式,拼接式,或下划线表示法使用[]或逗号分隔值的标准列表语法yaml文件驼峰式,拼接式,或下划线表示法标准的YAML列表语法或逗号分隔的值环境变量以下划线作为分隔符的大写格式。不应在属性名中使用_下划线包围的数值,如MY_ACME_1_OTHER = my.acme[1].other系统属性驼峰式,拼接式,或下划线表示法使用[]或逗号分隔值的标准列表语法官方建议将属性存储为小写的kebab格式,例如my.property-name=acme 当绑定到映射属性时,如果键包含除小写字母-数字字符或-之外的任何内容,则需要使用括号符号,以便保留原始值。如果键没有被[]包围,则删除任何不是字母数字或-的字符。例如,考虑将以下属性绑定到映射
acme: map: "[/key1]": value1 "[/key2]": value2 /key3: value3 上面的属性将绑定到以/key1、/key2和key3作为映射中的键的映射2.8.3)合并复杂类型 当列表在多个位置配置时,通过替换整个列表覆盖工作。 例如,假设MyPojo对象的名称和描述属性默认为null。下面的示例公开了AcmeProperties中的MyPojo对象列表
@ConfigurationProperties("acme") public class AcmeProperties { private final List<MyPojo> list = new ArrayList<>(); public List<MyPojo> getList() { return this.list; } } 所对应的yml文件配置如下: acme: list: - name: my name description: my description --- spring: profiles: dev acme: list: - name: my another name 如果dev配置文件没有激活,则AcmeProperties.list包含一个MyPojo对象,如之前定义的。如果开启dev配置文件了,然而还是会只包含一个,因为第二个描述为null,所以不能合并。如果希望合并需要如下例子: 当在多个概要文件中指定列表时,将使用具有最高优先级的列表(且仅使用该列表) acme: list: - name: my name description: my description - name: another name description: another description --- spring: profiles: dev acme: list: - name: my another name对于Map属性,可以使用从多个源绘制的属性值进行绑定。但是,对于多个源中的相同属性,使用优先级最高的属性。下面的示例公开了来自AcmeProperties的Map<String, MyPojo>
@ConfigurationProperties("acme") public class AcmeProperties { private final Map<String, MyPojo> map = new HashMap<>(); public Map<String, MyPojo> getMap() { return this.map; } } yml文件配置如下: 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 name1,如果开启dev配置则会有2个前面的合并规则适用于来自所有属性源的属性,而不仅仅是YAML文件
2.8.4)属性转换 当Spring Boot绑定到@ConfigurationProperties bean时,它尝试强制外部应用程序属性为正确的类型。如果需要自定义类型转换,可以提供一个ConversionService bean(使用一个名为ConversionService的bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义转换器(使用注释为@ConfigurationPropertiesBinding的bean定义) 由于此bean在应用程序生命周期的早期就被请求,所以请确保限制您的ConversionService正在使用的依赖项。通常,您需要的任何依赖项可能在创建时没有完全初始化。如果配置键强制转换不需要重命名自定义转换服务,并且只依赖于使用@ConfigurationPropertiesBinding限定的自定义转换程序,则可能希望重命名自定义转换服务
2.8.4.1)转换时间 Spring Boot专门支持表示持续时间。如果您公开java.time。持续时间属性,应用程序属性中的下列格式可用 java.time使用的标准ISO-8601格式的常规长表示(使用毫秒作为默认单位,除非指定了@DurationUnit)。持续时间一种可读性更好的格式,其中值和单元是耦合的(例如,10s表示10秒)
@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; } } 要指定30秒的会话超时,30秒、PT30S和30s都是等效的。 读取超时为500ms可以指定为以下任何一种形式:500、PT0.5S和500ms默认单元是毫秒,可以使用@DurationUnit重写,如上面的示例所示 如果要从以前的版本升级到使用Long来表示持续时间,那么请确保在切换到持续时间的同时定义单元(使用@DurationUnit)(如果不是毫秒)。这样做提供了一个透明的升级路径,同时支持更丰富的格式
2.8.4.2) 转换数据大小 Spring框架具有允许以字节表示大小的DataSize值类型。如果公开一个DataSize属性,应用程序属性中可以使用以下格式: 常规的长表示(使用字节作为默认单位,除非指定了@DataSizeUnit) 一种可读性更好的格式,其中值和单元是耦合的(例如10MB表示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兆字节的缓冲区大小,10和10MB是等效的。 大小阈值256字节可以指定为256或256B默认单元是字节,可以使用@DataSizeUnit重写,如上面的示例所示 如果您正在升级以前的版本,该版本只是简单地使用Long来表示大小,那么请确保定义了单元(使用@DataSizeUnit),如果它不是要进行数据化的开关旁边的字节。这样做提供了一个透明的升级路径,同时支持更丰富的格式
2.8.5)@ConfigurationProperties校验 每当用Spring的@Validated注释注释@ConfigurationProperties类时,Spring Boot都会尝试验证它们。您可以使用JSR-303 javax。直接在配置类上使用验证约束注释。为此,请确保在类路径上有一个兼容的JSR-303实现,然后向字段添加约束注释,如下面的示例所示
@ConfigurationProperties(prefix="acme") @Validated public class AcmeProperties { @NotNull private InetAddress remoteAddress; // ... getters and setters } 通过使用@Validated注释创建配置属性的@Bean方法来触发验证虽然在绑定时也会验证嵌套属性,但是最好将关联字段标注为@Valid。这确保即使没有找到嵌套属性也会触发验证。下面的示例基于前面的AcmeProperties示例
@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 } }还可以通过创建名为configurationPropertiesValidator的bean定义来添加自定义Spring验证器。@Bean方法应该声明为静态的。配置属性验证器是在应用程序生命周期的早期创建的,将@Bean方法声明为静态方法可以创建bean,而不必实例化@Configuration类。这样做可以避免早期实例化可能导致的任何问题。这里有一个属性验证示例,演示如何设置。 spring-boot-actuator模块包含一个端点,该端点公开所有@ConfigurationProperties bean将web浏览器指向/执行器/configprops或使用等效的JMX端点
2.8.6)@ConfigurationProperties vs. @Value @Value注释是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了@ConfigurationProperties和@Value支持的特性 如果您为自己的组件定义了一组配置键,我们建议您将它们分组到一个带有@ConfigurationProperties注释的POJO中。您还应该注意,由于@Value不支持放松绑定,如果您需要使用环境变量来提供值,那么它不是一个好的选择。 最后,虽然可以在@Value中编写SpEL表达式,但是这些表达式并不从应用程序属性文件(即application.yml等)中处理。