
Spring Boot 版本 2.7.8 原文:https://docs.spring.io/spring-boot/docs/2.7.8/reference/htmlsingle/ — 笔者注:
Spring Boot 3.x 第一个 GA 版本已于22年底发布,2.7.8 是2.x 的最后一个 GA版本,我觉得一段时间内依然会以2.x为主
本节对Spring Boot进行介绍 以及如何安装,我们将引导您构建第一个Spring Boot 应用,同时讨论一些核心准则。
Spring Boot 帮助您创建可以独立运行的,生产级的Spring 应用程序。
您创建的Spring Boot 应用程序,可以通过java -jar 或者 传统的war包方式启动,另外还提供了一个运行spring scripts的命令行工具。
Spring Boot 2.7.8 需要Java8 ,兼容Java19,Spring 版本5.3.25或更高。
构建工具 | 版本 |
|---|---|
Maven | 3.5+ |
Gradle | 6.8.x, 6.9.x, and 7.x |
Spring Boot 支持如下嵌入式servlet容器:
名称 | Servlet 版本 |
|---|---|
Tomcat 9.0 | 4.0 |
Jetty 9.4 | 3.1 |
Jetty 10.0 | 4.0 |
Undertow 2.0 | 4.0 |
您也可以将Spring Boot部署到任何兼容servlet 3.1+的容器中。
安装之前使用java -version检查 Java 版本,Spring Boot 2.7.8 需要Java8 或更高的版本。
Spring Boot 依赖项使用org.springframework.boot groupId。通常,您的Maven POM文件继承自spring-boot-starter-parent ,并声明一个或多个“Starters”的依赖关系。
另外Spring Boot 还提供了可选的Maven 插件来创建可执行的jar,更多信息参考 Spring Boot Maven 插件文档。
同Maven,Spring Boot 也提供了一个 Gradle插件,用于创建可执行的jar,更多信息参考 Spring Boot Gradle 插件文档。
Spring Boot CLI是一个命令行工具,可用于快速创建Spring Boot 初始化应用程序,这在没有IDE的情况下非常有用。
您可以从如下地址下载Spring CLI发行版本:
另外提供了 快照列表
下载后,按照解压缩存档中的INSTALL.txt说明进行操作。.zip文件的bin/目录中有一个spring脚本(适用于Windows的spring.bat),或者可以使用jar -jar 运行 jar包。
SDKMAN(软件开发工具包管理器)可用于管理各种二进制SDK版本,包括Groovy和Spring Boot CLI。从sdkman.io获取并使用以下命令安装 Spring Boot:
$ sdk install springboot
$ spring --version
Spring CLI v2.7.8如果您为 CLI 开发功能并希望访问您构建的版本,请使用以下命令:
$ sdk install springboot dev /path/to/spring-boot/spring-boot-cli/target/spring-boot-cli-2.7.8-bin/spring-2.7.8/
$ sdk default springboot dev
$ spring --version
Spring CLI v2.7.8前面的说明安装了一个spring名为instance 的本地dev实例。它指向您的目标构建位置,因此每次您重建 Spring Boot 时,spring它都是最新的。
您可以通过运行以下命令来查看它:
$ sdk ls springboot
================================================================================
Available Springboot Versions
================================================================================
> + dev
* 2.7.8
================================================================================
+ - local version
* - installed
> - currently in use
================================================================================在Mac上可以使用Homebrew安装。
$ brew tap spring-io/tap
$ brew install spring-bootHomebrew 安装 spring 到 /usr/local/bin.
如果找不到这个命令,尝试使用
brew update更新后重试
在Mac上使用MacPorts 安装。
$ sudo port install spring-boot-cli在Window使用Scoop安装。
> scoop bucket add extras
> scoop install springbootScoop 安装 spring 到 ~/scoop/apps/springboot/current/bin
如果提示命令不存,请使用
scoop update更新后再重试
首先创建一个名为app.groovy的文件。
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
"Hello World!"
}
}然后使用如下命令运行:
$ spring run app.groovy第一次运行需要下载依赖,会比较慢,后面运行会快很多。
最后,使用浏览器打开localhost:8080,输出
Hello World!建议使用start.spring.io 创建Spring Boot 应用程序。
从1.x升级,可以查看GitHub wiki上的升级指南
Spring Boot提供了一种方法来分析应用程序的环境并在启动时打印诊断信息,还可以在运行时临时迁移属性,要启动该功能,在项目中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>完成升级后,删除该依赖。
可以使用Maven、Gradle、Ant 构建系统
所有官方启动器都遵循类似的命名模式:spring-boot-starter-*,其中*是特定类型的应用程序,如果是自己创建的启动器一般以项目名称开头,如thirdpartyproject-spring-boot-starter。
Spring Boot 提供了以下应用启动器org.springframework.boot:
名称 | 描述 |
|---|---|
spring-boot-starter | Core starter,包括自动配置支持、日志记录和 YAML |
spring-boot-starter-activemq | 使用 Apache ActiveMQ 的 JMS 消息传递启动器 |
spring-boot-starter-amqp | 使用 Spring AMQP 和 Rabbit MQ 的启动器 |
spring-boot-starter-aop | 使用 Spring AOP 和 AspectJ 进行面向方面编程的入门 |
spring-boot-starter-artemis | 使用 Apache Artemis 的 JMS 消息传递启动器 |
spring-boot-starter-batch | 使用 Spring Batch 的启动器 |
spring-boot-starter-cache | 使用 Spring Framework 的缓存支持的 Starter |
spring-boot-starter-data-cassandra | 使用 Cassandra 分布式数据库和 Spring Data Cassandra 的 Starter |
spring-boot-starter-data-cassandra-reactive | 使用 Cassandra 分布式数据库和 Spring Data Cassandra Reactive 的 Starter |
spring-boot-starter-data-couchbase | 使用 Couchbase 面向文档的数据库和 Spring Data Couchbase 的启动器 |
spring-boot-starter-data-couchbase-reactive | 使用 Couchbase 面向文档的数据库和 Spring Data Couchbase Reactive 的 Starter |
spring-boot-starter-data-elasticsearch | 使用 Elasticsearch 搜索和分析引擎以及 Spring Data Elasticsearch 的 Starter |
spring-boot-starter-data-jdbc | 使用 Spring Data JDBC 的启动器 |
spring-boot-starter-data-jpa | 将 Spring Data JPA 与 Hibernate 一起使用的启动器 |
spring-boot-starter-data-ldap | 使用 Spring Data LDAP 的启动器 |
spring-boot-starter-data-mongodb | 使用 MongoDB 面向文档的数据库和 Spring Data MongoDB 的启动器 |
spring-boot-starter-data-mongodb-reactive | 使用 MongoDB 文档型数据库和 Spring Data MongoDB Reactive 的 Starter |
spring-boot-starter-data-neo4j | 使用 Neo4j 图形数据库和 Spring Data Neo4j 的启动器 |
spring-boot-starter-data-r2dbc | 使用 Spring Data R2DBC 的启动器 |
spring-boot-starter-data-redis | 用于将 Redis 键值数据存储与 Spring Data Redis 和 Lettuce 客户端一起使用的 Starter |
spring-boot-starter-data-redis-reactive | 将 Redis 键值数据存储与 Spring Data Redis 反应式和 Lettuce 客户端一起使用的启动器 |
spring-boot-starter-data-rest | 使用 Spring Data REST 通过 REST 公开 Spring Data 存储库的 Starter |
spring-boot-starter-freemarker | 使用 FreeMarker 视图构建 MVC Web 应用程序的启动器 |
spring-boot-starter-graphql | 使用 Spring GraphQL 构建 GraphQL 应用程序的 Starter |
spring-boot-starter-groovy-templates | 使用 Groovy 模板视图构建 MVC web 应用程序的启动器 |
spring-boot-starter-hateoas | 使用 Spring MVC 和 Spring HATEOAS 构建基于超媒体的 RESTful Web 应用程序的启动器 |
spring-boot-starter-integration | 使用 Spring Integration 的启动器 |
spring-boot-starter-jdbc | 将 JDBC 与 HikariCP 连接池一起使用的启动器 |
spring-boot-starter-jersey | 使用 JAX-RS 和 Jersey 构建 RESTful Web 应用程序的启动器。的替代品spring-boot-starter-web |
spring-boot-starter-jooq | 使用 jOOQ 通过 JDBC 访问 SQL 数据库的启动器。替代spring-boot-starter-data-jpa或spring-boot-starter-jdbc |
spring-boot-starter-json | 读写json的starter |
spring-boot-starter-jta-atomikos | 使用 Atomikos 的 JTA 事务启动器 |
spring-boot-starter-mail | 使用 Java Mail 和 Spring Framework 的电子邮件发送支持的 Starter |
spring-boot-starter-mustache | 使用 Mustache 视图构建 Web 应用程序的启动器 |
spring-boot-starter-oauth2-client | 使用 Spring Security 的 OAuth2/OpenID Connect 客户端功能的 Starter |
spring-boot-starter-oauth2-resource-server | 使用 Spring Security 的 OAuth2 资源服务器功能的启动器 |
spring-boot-starter-quartz | 使用 Quartz 调度器的启动器 |
spring-boot-starter-rsocket | 用于构建 RSocket 客户端和服务器的启动器 |
spring-boot-starter-security | 使用 Spring Security 的启动器 |
spring-boot-starter-test | 用于使用 JUnit Jupiter、Hamcrest 和 Mockito 等库测试 Spring Boot 应用程序的 Starter |
spring-boot-starter-thymeleaf | 使用 Thymeleaf 视图构建 MVC Web 应用程序的启动器 |
spring-boot-starter-validation | 将 Java Bean Validation 与 Hibernate Validator 结合使用的 Starter |
spring-boot-starter-web | 用于使用 Spring MVC 构建 Web(包括 RESTful)应用程序的 Starter。使用 Tomcat 作为默认的嵌入式容器 |
spring-boot-starter-web-services | 使用 Spring Web 服务的启动器 |
spring-boot-starter-webflux | 用于使用 Spring Framework 的 Reactive Web 支持构建 WebFlux 应用程序的 Starter |
spring-boot-starter-websocket | 使用 Spring Framework 的 MVC WebSocket 支持构建 WebSocket 应用程序的 Starter |
除了应用程序启动器之外,还可以使用以下启动器来添加*生产就绪*功能:
名称 | 描述 |
|---|---|
spring-boot-starter-actuator | 使用 Spring Boot Actuator 的 Starter,它提供生产就绪功能来帮助您监控和管理您的应用程序 |
最后,Spring Boot 还包括以下启动器:
名称 | 描述 |
|---|---|
spring-boot-starter-jetty | 使用 Jetty 作为嵌入式 servlet 容器的启动器的替代品spring-boot-starter-tomcat |
spring-boot-starter-log4j2 | 使用 Log4j2 进行日志记录的启动器的替代品spring-boot-starter-logging |
spring-boot-starter-logging | 使用 Logback 进行日志记录的启动器。默认日志记录启动器 |
spring-boot-starter-reactor-netty | 使用 Reactor Netty 作为嵌入式响应式 HTTP 服务器的启动器。 |
spring-boot-starter-tomcat | 将 Tomcat 用作嵌入式 servlet 容器的启动器。使用的默认 servlet 容器启动器spring-boot-starter-web |
spring-boot-starter-undertow | 使用 Undertow 作为嵌入式 servlet 容器的启动器的替代品spring-boot-starter-tomcat |
其他社区贡献的starter列表,请参阅GitHub 上模块 中的自述文件。
spring-boot-starters
Spring Boot 没有固定的代码布局,但是有些实践提供参考。
当一个类不包含package时,它被认为在“default package”中。通常不建议使用“default package”,它可能会导致@ComponentScan、@ConfigurationPropertiesScan、@EntityScan或@SpringBootApplication 注解出现问题,我们应该遵循推荐的包命名方式,比如com.example.project
通常建议将主程序类放在其他类之上的根包中,@SpringBootApplication通常放在主类中,其隐式的定义了基本的包搜索功能,其内部引入了@EnableAutoConfiguration和@ComponentScan。
下面是一个典型的布局:
com
+- example
+- myapplication
+- MyApplication.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.javaMyApplication.java 定义了一个main方法以及@SpringBootApplication,如下所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}Spring Boot 支持使用Java进行配置,虽然SpringApplication 跟XML可以一起使用,但还是建议@Configuration是独立的类。
你不需要把所有的@Configuration 放到一个类中,该@Import注解可用于导入其他的配置类,或者可以使用@ComponentScan自动获取所有的Spring 组件,包括@Configuration类。
如果你还是要使用XML配置,依然建议使用@Configuration类,然后使用@ImportResource来加载XML配置。
Spring Boot会尝试将starter自动配置到应用程序,比如引入了HSQLDB的starter,但是没有手动配置任何数据库连接bean,那么Spring Boot 会自动配置一个内存数据库。
开启自动配置,需要添加@EnableAutoConfiguration或者@SpringBootApplication。
自动配置是非侵入式的,任何时候都可以使用自定义配置替换自动配置的指定部分,比如,添加了DataSource bean,默认的嵌入式数据库就会被替换。
使用--debug启动应用程序,可以打印出当前应用了哪些自动配置。
如果想要禁用指定的自动配置类,可以使用@SpringBootApplication的exclude属性,如:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
}如果排除的类不在类路径中,可以使用excludeName指定类的完全限定名,另外如果不用@SpringBootApplication,@EnableAutoConfiguration的exclude和excludeName也是可用的。
最后也能用spring.autoconfigure.exclude的配置来排除自动配置类。
通常建议使用构造函数注入依赖项,和@ComponentScan查找bean。
如果是按照4.2的方式构建的代码,则可以使用@ComponentScan不带任何参数或者使用@SpringBootApplication其已经包含了@ComponentScan注解,这样所有的组件(@Component、@Service、@Repository、@Controller和其他)都会自动注册为Spring Beans。
如下示例表示一个@Service使用构造函数来注入RiskAssessor Bean。
import org.springframework.stereotype.Service;
@Service
public class MyAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public MyAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}如果一个Bean有多个构造函数,需要使用@Autowired标记哪个需要Spring 注入:
import java.io.PrintStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyAccountService implements AccountService {
private final RiskAssessor riskAssessor;
private final PrintStream out;
@Autowired
public MyAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
this.out = System.out;
}
public MyAccountService(RiskAssessor riskAssessor, PrintStream out) {
this.riskAssessor = riskAssessor;
this.out = out;
}
// ...
}使用构造函数注入应该使用
final标记,表示后面不能再被修改。
使用@SpringBootApplication注解可以启用如下三个功能:
@EnableAutoConfiguration: 启用Spring Boot 的自动配置机制
@ComponentScan @Component:在应用程序所在的包上启用扫描
@SpringBootConfiguration: 允许在上下文中注册额外的 beans 或导入额外的配置类。Spring 标准的替代方案@Configuration,有助于在集成测试中进行配置检测。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}如果不想使用@SpringBootApplication,也可以单独使用注解,如下示例并未使用@ComponentScan 自动扫描功能,而使用显示导入(@Import):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Import;
@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ SomeConfiguration.class, AnotherConfiguration.class })
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}使用java -jar运行:
$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar也可以附加远程调式器:
$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \
-jar target/myapplication-0.0.1-SNAPSHOT.jarSpring Boot Maven 插件包含一个run命令:
$ mvn spring-boot:run另外还可以使用MAVEN_OPTS 设置环境变量:
$ export MAVEN_OPTS=-Xmx1024mGradle插件包含一个bootRun命令:
$ gradle bootRun使用JAVA_OPTS设置环境变量:
$ export JAVA_OPTS=-Xmx1024mSpring Boot 的热插拨基于JVM,JVM在某种程序上受限于可以替换的字节码,对于完整方案可以使用JRebel 。
spring-boot-devtools模块还包括对应用程序快速重启的支持,详细信息查看后面的热插拔“操作方法”。
Spring Boot 提供spring-boot-devtools 模块提供开发时的额外功能,要支持该功能,需要将依赖添加到项目中:
Maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>Gradle
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
}默认情况下,打包的应用程序不包含devtools,如果想要使用某个远程devtool特性,在Maven插件中配置,excludeDevtools为false,Gradle插件中配置task任务以包含developmentOnly,如
tasks.named("bootWar") {
classpath configurations.developmentOnly
}当打包生产应用程序时,开发者工具将被自动禁用。如果您的应用程序是从一个特殊的类加载器启动的或者 使用
java -jar,那么会被认为是一个"生产的应用程序"。可以使用spring.devtools.restart.enabled来控制,要开启devtools,使用-Dspring.devtools.restart.enabled=true启动,要禁用devtools,排除依赖项或者使用-Dspring.devtools.restart.enabled=false启动。
Maven 中使用optional,Gradle 使用 developmentOnly,表示可以防止devtools被传递到项目的其他模块。
开发者工具的重启功能是通过使用两个类加载器实现的,对于大不多应用程序效果很好,但是有时候会导致类加载问题,特别是在多模块项目中。
要判断是不是由于这个问题,可以尝试禁用重启,使用spring.devtools.restart.enabled=false属性禁用它。
另外可以 自定义重启类加载器,自定义由哪个类加载加载,详见[4.8.3自动重启](#4.8.3 自动重启)。
Spring Boot 的一些库使用缓存来提高性能,比如,模版引擎会缓存编译后的模版,以此避免重复解析,但这样在开发过程中我们就不能即时看到模版的变更。spring-boot-devtools 默认禁用了缓存。
下表列出了所有应用的属性:
名称 | 默认值 |
|---|---|
server.error.include-binding-errors | always |
server.error.include-message | always |
server.error.include-stacktrace | always |
server.servlet.jsp.init-parameters.development | true |
server.servlet.session.persistent | true |
spring.freemarker.cache | false |
spring.graphql.graphiql.enabled | true |
spring.groovy.template.cache | false |
spring.h2.console.enabled | true |
spring.mustache.servlet.cache | false |
spring.mvc.log-resolved-exception | true |
spring.template.provider.cache | false |
spring.thymeleaf.cache | false |
spring.web.resources.cache.period | 0 |
spring.web.resources.chain.cache | false |
如果不想应用属性默认值,可以在应用程序配置文件中配置
spring.devtools.add-properties=false
在开发WEB应用的时候,可以开启DEBUG日志,这样会显示请求、正在处理的程序,响应结果和其他详细信息,如果希望显示所有的详细信息(比如潜在的敏感信息),可以打开spring.mvc.log-request-details或spring.codec.log-request-details。
⚠️笔者注: 开启spring.mvc.log-request-details 后的日志

关闭spring.mvc.log-request-details后的日志:

只要类路径上的文件发生变更,使用了spring-boot-devtools的应用程序就会自动重启,但是某些资源(如静态资源和视图模版)不需要重启应用程序。
触发重启的方法: 由于DevTools 通过监听类路径上的资源来触发重启,所以不管使用哪个IDE都需要重新编译后才能触发重启: Eclipse 中,保存修改后会更新类文件并触发重启 IDEA中,通过Build 触发或者编辑项目的
Edit Configurations -> On Update action:Update classes and resources也可以触发重启 使用构建工具,mvn compile或者gradle build可以触发重启⚠️笔者注: 官方文档提示:使用Maven或者Gradle时,需要将
forking设置为enabled,才能触发重启。 实测,新版本的spring-boot-maven-plugin在项目引入spring-boot-devtools后会自动开启fork,如图:

并且插件的注释也标记为过期,将在3.0.0中彻底删除:

在重启期间 DevTools 依赖应用上下文的 shutdown hook 来关闭,如果设置为SpringApplication.setRegisterShutdownHook(false),就会导致其无法正常工作。
⚠️笔者注:
在笔者按照这样设置后,发现自动重启并无失效
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringBootDemoApplication.class);
application.setRegisterShutdownHook(false);
application.run(args);
}AspectJ 切面不支持自动重启
重新启动与重新加载
Spring Boot 的重启技术通过使用两个类加载器来工作的,不会更改的类(如:第三方jar的类)被加载到基类加载器中,频繁修改的类被加载到一个重启类加载器中。当应用程序重启时,旧的重启类加载器被丢弃并创建一个新的类加载器,这种方法会被“冷启动”快得多,因为基类加载器已经可用。
如果自动重启还是比较慢的,或者遇到类加载问题,可用尝试使用重新加载技术,如JRebel,他们通过加载类时重写类来获得更快的速度。
默认每次自动重启应用程序的时候,都会显示一份对自动配置的变更报告(比如添加或删除bean或者设置配置属性)
禁用报告设置:
spring.devtools.restart.log-condition-evaluation-delta=false⚠️笔者注: 开启时候的报告示例:

某些资源在更改时并不会触发自动重启,默认情况下更改 /META-INF/maven, /META-INF/resources, /resources, /static, /public, /templates目录下的资源不会触发重启但是会触发[实时加载](#4.8.4 实时加载),如果要自定义这些排除项,可以使用spring.devtools.restart.exclude属性,比如仅排除/static和/public目录:
spring.devtools.restart.exclude=static/**,public/**如果要保留默认的配置,并且添加新的排除项,使用spring.devtools.restart.additional-exclude。
如果要监听不在类路径中的文件时,使用spring.devtools.restart.additional-paths属性。另外可以配合spring.devtools.restart.exclude来设置其他路径下的文件变更是触发重启还是实时加载。
使用spring.devtools.restart.enabled禁用重启,如果在application.properties配置,重启类加载器还是会初始化,只是不会监听文件的变更,要完全禁用需要设置系统变量spring.devtools.restart.enabled为false,如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApplication.class, args);
}
}使用某个指定的文件变更来触发自动重启,使用spring.devtools.restart.trigger-file配置指定文件(不包括路径),该文件必须在类路径下。
比如:有这样一个结构的项目:
src
+- main
+- resources
+- .reloadtrigger那么trigger-file的配置是
spring.devtools.restart.trigger-file=.reloadtrigger默认情况下,IDE中打开的项目都使用重启类加载器,其他.jar文件使用基类加载器。使用mvn spring-boot:run或者gradle bootRun也是这样。
可以通过META-INF/spring-devtools.properties文件来自定义,spring-devtools.properties文件包含前缀为restart.exclude和restart.include的属性,include属性被重启类加载器加载,exclude属性被基类加载器排除,该属性适用类路径的正则表达式,如:
restart.exclude.companycommonlibs=/mycorp-common-[\\w\\d-\\.]+\\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w\\d-\\.]+\\.jar键必须是唯一的,只要是
restart.exclude和restart.include开头的属性都会被考虑。META-INF/spring-devtools.properties的内容可以打包中项目中,也可以打包到库中。
对于使用标准ObjectInputStream反序列化的对象,重新启动功能不起作用。如果您需要反序列化数据,则可能需要将Spring的ConfigurableObjectInputStream与Thread.currentThread().getContextClassLoader()结合使用。
⚠️笔者注: 这个点我觉得略过即可
spring-boot-devtools包含一个嵌入式的LiveReload服务器,可用于资源变更时实时触发浏览器刷新。LiveReload 浏览器扩展可从livereload.com免费获得 Chrome、Firefox 和 Safari 。如果您不想在应用程序运行时启动 LiveReload 服务器,您可以将该spring.devtools.livereload.enabled属性设置为false.
您一次只能运行一个 LiveReload 服务器。在启动您的应用程序之前,请确保没有其他 LiveReload 服务器正在运行。如果您从 IDE 启动多个应用程序,则只有第一个应用程序支持 LiveReload。
⚠️笔者注: 这个点我觉得略过即可,浏览器手动刷新一下也不费事🤪
可以通过将以下任何文件添加到$HOME/.config/spring-boot目录来配置全局 devtools 设置:
spring-boot-devtools.propertiesspring-boot-devtools.yamlspring-boot-devtools.yml添加到该文件的任何配置都适用于该机器上的所有Spring Boot 应用程序,例如,要将自动重启配置为使用触发式文件,可以这样配置:
spring.devtools.restart.trigger-file=.reloadtrigger默认情况下,$HOME是用户的主目录。要自定义此位置,请设置SPRING_DEVTOOLS_HOME环境变量或spring.devtools.home系统属性。
如果在HOME/.config/spring-boot中找不到 devtools 配置文件,则会在根HOME目录中搜索是否存在.spring-boot-devtools.properties文件。这允许您与不支持该
在
.spring-boot-devtools.properties中的配置都不会影响其他的应用配置文件(如application-{profile}之类的文件),并且不支持spring-boot-devtools-.properties和spring.config.activate.on-profile 之类的配置。
FileSystemWatcher通过一定的时间间隔轮询类文件的变更来工作,然后等待预定义的静默期以确保没有更多变更。如果您发现有时候某些更改并没有及时变化,可以尝试修改spring.devtools.restart.poll-interval和spring.devtools.restart.quiet-period参数。
spring.devtools.restart.poll-interval=2s
spring.devtools.restart.quiet-period=1s受监视的类路径目录现在每 2 秒轮询一次更改,并保持 1 秒的静默期以确保没有其他类更改。
Spring Boot 支持部分远程功能,但有一定安全风险,只能在受信任的网络或SSL保护下运行,并且不能在生产环境上开启该功能。
启用该功能,确保如下配置:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>然后使用spring.devtools.remote.secret设置一个复杂的密码。
Spring WebFlux 不支持该功能
远程客户端应用程序旨在从IDE中运行。您需要使用与连接到的远程项目相同的类路径运行org.springframework.boot.devtools.RemoteSpringApplication。应用程序的单个必需参数是它连接的远程URL。
例如,如果您使用的是Eclipse或STS,并且已经部署到Cloud Foundry的项目名为my-app,则可以执行以下操作:
Run菜单中选择Run Configurations…。Java Application“启动配置”。my-app项目。org.springframework.boot.devtools.RemoteSpringApplication作为主类。https://myapp.cfapps.io添加到Program arguments(或任何远程URL)。正在运行的远程客户端可能类似于以下列表:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: (v2.7.8)
2023-01-19 14:18:32.205 INFO 16947 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication v2.7.8 using Java 1.8.0_362 on myhost with PID 16947 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/2.7.8/spring-boot-devtools-2.7.8.jar started by myuser in /opt/apps/)
2023-01-19 14:18:32.211 INFO 16947 --- [ main] o.s.b.devtools.RemoteSpringApplication : No active profile set, falling back to 1 default profile: "default"
2023-01-19 14:18:32.566 INFO 16947 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2023-01-19 14:18:32.584 INFO 16947 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 0.804 seconds (JVM running for 1.204)因为远程客户端使用与真实应用程序相同的类路径,所以它可以直接读取应用程序属性。这是
spring.devtools.remote.secret属性的读取方式并传递给服务器进行身份验证。始终建议使用
https://作为连接协议,以便加密连接并且不会截获密码。 如果需要使用代理来访问远程应用程序,请配置spring.devtools.remote.proxy.host和spring.devtools.remote.proxy.port属性。
远程客户端以与[本地重新启动](#4.8.3 自动重启)相同的方式监视应用程序类路径以进行更改 。任何更新的资源都会被推送到远程应用程序,并且(如果需要)会触发重新启动。如果您迭代使用本地没有的云服务的功能,这将非常有用。通常,远程更新和重新启动比完全重建和部署周期快得多。
仅在远程客户端运行时监视文件。如果在启动远程客户端之前更改文件,则不会将其推送到远程服务器
⚠️笔者注: 对于目前的大型微服务集群来说,并不实用,而且操作繁琐,使用这种更新方式部分类还有可能不生效,如果只是在测试环境使用,还不如Jenkins重新打包部署
使用Maven 或者Gradle 打包应用程序,生成jar包文件。
SpringApplication提供了一个main()方法方便引导Spring 应用程序启动,并委托给静态方法SpringApplication.run。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}启动后,会看到如下信息:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.8)
2023-01-19 14:18:33.375 INFO 17059 --- [ main] o.s.b.d.f.s.MyApplication : Starting MyApplication using Java 1.8.0_362 on myhost with PID 17059 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-01-19 14:18:33.379 INFO 17059 --- [ main] o.s.b.d.f.s.MyApplication : No active profile set, falling back to 1 default profile: "default"
2023-01-19 14:18:34.288 INFO 17059 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-01-19 14:18:34.301 INFO 17059 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-01-19 14:18:34.301 INFO 17059 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.71]
2023-01-19 14:18:34.371 INFO 17059 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-01-19 14:18:34.371 INFO 17059 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 943 ms
2023-01-19 14:18:34.754 INFO 17059 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-01-19 14:18:34.769 INFO 17059 --- [ main] o.s.b.d.f.s.MyApplication : Started MyApplication in 1.789 seconds (JVM running for 2.169)默认情况下日志级别是INFO,如果需要额外的日志级别设置,查看[5.4.5 日志级别](#5.4.5 日志级别)。通过spring.main.log-startup-info设置为false,可以关闭应用程序的日志记录。
如果应用启动失败,能够通过已注册的FailureAnalyzers获取错误信息以便修复问题。比如应用程序启动的8080端口被占用。
***************************
APPLICATION FAILED TO START
***************************
Description:
Embedded servlet container failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.Spring Boot 支持自定义
FailureAnalyzer实现
如果没有故障分析器能够处理异常,您需要给org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener[启用debug属性](#5.2 外部配置),或者[开启DEBUG日志](#5.4.5 日志级别)。
使用java -jar启动应用程序,使用debug开启日志:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debugSpringApplication 允许延迟初始化应用程序,当启用惰性初始化时,bean 在需要时创建,而不是在启动期间创建。
惰性初始化的一个缺点是会延迟发现应用程序的问题,如果配置错误的bean被惰性初始化,则在启动期间不会发生故障,只有在bean 被初始化时才发现问题。另外还要注意确保JVM有足够的内存来容纳所有的bean。因此建议在启用惰性初始化前微调JVM堆大小。
使用SpringApplicationBuilder的lazyInitialization或者SpringApplication的setLazyInitialization方法开启惰性初始化,也可以使用spring.main.lazy-initialization开启。
spring.main.lazy-initialization=true指定某些bean延迟初始化,使用
@Lazy(false)
通过将banner.txt添加到类路径中,或者设置spring.banner.location为该类文件的位置,来更改应用启动时打印的横幅。
如果文件编码不是UTF-8,可以设置spring.banner.charset。
除了使用文本文件外,还可以使用图片,将图片添加到类路径中,或者设置spring.banner.image.location,图形将被转换为ASCII格式。
在banner.txt文件中,您可以使用Environment中可用的任何键和以下占位符。
占位符 | 描述 |
|---|---|
${application.version} | 您的应用程序的版本号,如在MANIFEST.MF声明的那样。例如,Implementation-Version: 1.0打印为1.0. |
${application.formatted-version} | 您的应用程序的版本号,在MANIFEST.MF中声明并格式化显示(用括号括起来并以 为前缀v)。例如(v1.0)。 |
${spring-boot.version} | 您正在使用的 Spring Boot 版本。例如2.7.8。 |
${spring-boot.formatted-version} | 您正在使用的 Spring Boot 版本,经过格式化以供显示(用方括号括起来并以 为前缀v)。例如(v2.7.8)。 |
${Ansi.NAME}(或${AnsiColor.NAME},,${AnsiBackground.NAME})${AnsiStyle.NAME} | NAMEANSI 转义代码的名称在哪里。详情请见AnsiPropertySource。 |
${application.title} | 您的应用程序的标题,如MANIFEST.MF中声明的那样。例如Implementation-Title: MyApp打印为MyApp. |
使用
SpringApplication.setBanner(…)以编程方式设置横幅,使用org.springframework.boot.Banner接口并实现printBanner()方法自定义打印横幅。
可以使用spring.main.banner-mode 设置是否在System.out( console)、或者日志文件中打印横幅、或者不打印横幅
{application.version}和{application.formatted-version}配置仅仅在使用Spring Boot启动器的时候可用。如果你使用未打包的jar并使用java -cp <classpath> <mainclass>启动,则不会生效。 这就是为什么我们建议您始终使用java org.springframework.boot.loader.JarLauncher启动未打包的jar。这将在构建类路径和启动应用程序之前初始化
application.*banner变量。
如果默认的SpringApplication 不适合您,您可以自己创建一个实例,并进行自定义。例如,要关闭横幅:
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}也可以使用application.properties配置SpringApplication,详细查看[外部配置](#5.2 外部配置)
如果您需要构建ApplicationContext层次结构(具有父子关系的多个上下文)或者更喜欢使用流式API构建器,可以使用SpringApplicationBuilder。
SpringApplicationBuilder让你将多个方法链式调用,包括parent和child方法创建层级结构,比如:
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);ApplicationContext创建层次结构 时有一些限制。例如,Web 组件必须包含在子上下文中,并且同样Environment用于父上下文和子上下文。有关详细信息,请参阅SpringApplicationBuilderJavadoc。
当部署在平台上时,应用程序可以使用Kubernetes Probe等基础设施向平台提供有关其可用性的信息。
Spring Boot 对常用的“liveness” 和 “readiness”状态提供开箱即用的支持。如果您使用了Spring Boot 的actuator那么状态将以监控端点的形式提供。
另外,您还可以通过ApplicationAvailability接口将可用性状态注入到自己的bean中。
应用程序的"Liveness"状态表示其是否能正常工作,或者当前是失败状态,则自行修复。中断的“Liveness”状态意味着应用程序处于无法恢复的状态,那么基础架构应重启应用程序。
“Liveness”状态不应该基于外部检查,比如健康检查。如果这样,一个失败的外部信息(如数据库、外部缓存等)将导致大规模重启和整个平台的连锁故障。
Spring Boot 应用程序的内部状态主要根据Spring 的 ApplicationContext。如果应用程序上下文成功启动,则Spring Boot 会认为应用程序处于有效状态,上下文刷新的话,应用程序被认为处于活跃,更多参考[5.1.7 应用程序事件和监听器](#5.1.7 应用程序事件和监听器)
“Readiness”状态表示应用程序是否已经准备好处理请求。失败的“Readiness”状态表示现在不应该接收流量。这通常发生在启动期间,同时处理CommandLineRunner和ApplicationRunner组件,或者在应用程序认为太忙的时候发生。一旦应用程序和使用命令行调用应用程序被调用,就被认为是“Readiness 状态”。
预期在启动期间运行的任务应该由组件
CommandLineRunner和ApplicationRunner执行,不是使用 Spring 组件生命周期回调,例如@PostConstruct
应用程序可用随时通过注入ApplicationAvailability 接口并调用其上的方法来获取其可用性状态。还有的情况是,应用程序希望监听状态更新或者更新应用程序的状态。
例如,我们可以将应用程序的“Readiness”状态导出到一个文件中,以便Kubernetes“exec Probe”可以查看该文件:
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyReadinessStateExporter {
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC:
// create file /tmp/healthy
break;
case REFUSING_TRAFFIC:
// remove file /tmp/healthy
break;
}
}
}当应用程序中断并且不能恢复的时候,还可以更新这个应用程序的状态。
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class MyLocalCacheVerifier {
private final ApplicationEventPublisher eventPublisher;
public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void checkLocalCache() {
try {
// ...
}
catch (CacheCompletelyBrokenException ex) {
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
}
}
}Spring Boot 提供[Kubernetes HTTP接口](#11.2.9 Kubernetes Probes),用于Actuator 健康端点的"Liveness" and "Readiness"状态,你能够获取更多指导关于[在Kubernetes上部署应用程序](#12.1.2 Kubernetes)。
除了Spring Framework事件之外,比如ContextRefreshedEvent,SpringApplication 还会发送一些额外的事件。
有些事件实际上是在创建
ApplicationContext创建之前,因此你不能作为@Bean注册监听器。你能够通过SpringApplication.addListeners(…)方法或者SpringApplicationBuilder.listeners(…)注册。 如果希望自动注册这些监听器,可以将监听器添加到META-INF/spring.factories中,使用org.springframework.context.ApplicationListener做为key。
运行应用程序的时候,按以下顺序发送事件:
ApplicationStartingEvent 在应用程序开始运行时发送(任何处理之前),除了监听器和初始化程序的注册ApplicationEnvironmentPreparedEvent发送,当上下文中要使用的已知Environment时但在创建上下文之前。ApplicationContextInitializedEvent发送,在准备了ApplicationContext并且调用了ApplicationContextInitializers后,但在加载任何bean之前ApplicationPreparedEvent在刷新开始之前,但在加载Bean定义后发送ApplicationStartedEvent在刷新上下文之后,但在任何应用程序和命令行程序被调用之前发送AvailabilityChangeEvent在表示应用程序状态为LivenessState.CORRECT时发送ApplicationReadyEvent在任何应用程序和命令行程序被调用之后发送AvailabilityChangeEvent 在表示应用程序已经做好接收请求准备时发送,状态为ReadinessState.ACCEPTING_TRAFFICApplicationFailedEvent在启动异常时发送上面的列表只包括与SpringApplication相关的SpringApplicationEvent事件。以下事件也在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发送。
WebServerInitializedEvent在WebServer准备好后发送,ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet 和 reactive的变体ContextRefreshedEvent在ApplicationContext刷新后发送事件监听器不应该运行冗长的任务,因为他们默认在同一线程中运行
应用程序事件使用Spring Framework的事件发布机制发送。此机制的一部分确保在子上下文中发布给监听器的事件也会在任何祖先上下文中发布给监听器。因此,如果您的应用程序使用SpringApplication实例的层次结构,则监听器可能会收到相同类型的应用程序事件的多个实例。
为了允许监听器区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现ApplicationContextAware或者如果监听器是bean,使用@Autowired来注入上下文。
SpringApplication会试图创建正确类型的ApplicationContext。用于确定WebApplicationType的算法如下:
AnnotationConfigServletWebServerApplicationContextAnnotationConfigReactiveWebServerApplicationContextAnnotationConfigApplicationContext这意味着如果您WebClient在同一应用程序中使用 Spring MVC 和 Spring WebFlux ,则默认情况下将使用 Spring MVC。您可以通过调用setWebApplicationType(WebApplicationType)来覆盖。
也可以完全控制ApplicationContext调用所使用的类型setApplicationContextClass(…)。
在JUnit测试中使用
SpringApplication时,通常需要调用setWebApplicationType(WebApplicationType.NONE)
如果您需要访问传递给SpringApplication.run(…)的应用程序参数,则可以注入org.springframework.boot.ApplicationArguments bean。ApplicationArguments接口提供对原始String[]参数以及解析的option和non-option参数的访问,如以下示例所示:
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}Spring Boot还注册
CommandLinePropertySource和SpringEnvironment。这使您还可以使用@Value注释注入单个应用程序参数。
如果您需要在启动后运行一些特定的代码SpringApplication,您可以实现ApplicationRunner或CommandLineRunner接口。这两个接口以相同的方式工作,并提供一个run方法,该方法在SpringApplication.run(…)完成之前被调用。
非常适合在应用程序启动后但在接受请求之前运行的任务
这些CommandLineRunner接口提供字符串数组用于访问对应用程序参数,而ApplicationRunner使用ApplicationArguments。以下示例显示了CommandLineRunner一个run方法:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}如果CommandLineRunner或ApplicationRunner bean必须按顺序调用,可以实现org.springframework.core.Ordered接口或者使用org.springframework.core.annotation.Order注解,
每个都SpringApplication向 JVM 注册一个关闭钩子,以确保ApplicationContext在退出时正常关闭。可以使用所有标准的 Spring 生命周期回调(例如DisposableBean接口或@PreDestroy注释)。
此外,如果希望在SpringApplication.exit()被调用时返回特定的退出代码,则可以实现该接口org.springframework.boot.ExitCodeGenerator,然后可以将此退出代码传递给System.exit()其将作为状态码返回,如以下示例所示:
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
}
}此外,ExitCodeGenerator接口可以通过异常来实现。遇到此类异常时,Spring Boot 会返回已实现getExitCode()方法提供的退出代码。
如果存在多个ExitCodeGenerator,则使用生成的第一个非零退出代码。要控制调用生成器的顺序,请另外实现org.springframework.core.Ordered接口或使用org.springfframework.core.annotation.order注解。
可以通过指定spring.application.admin.enabled属性为应用程序启用与管理相关的功能。这暴露了SpringApplicationAdminMXBean平台上的MBeanServer。您可以使用此功能远程管理您的 Spring Boot 应用程序。此功能也可用于任何服务包装器实现。
如果您想知道应用程序在哪个 HTTP 端口上运行,获取
local.server.port属性的值。
在应用程序启动期间,SpringApplication执行ApplicationContext许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件相关的任务。有了ApplicationStartupSpring Framework ,您就可以使用StartupStep对象跟踪应用程序启动顺序。可以收集这些数据用于分析目的,或者只是为了更好地了解应用程序启动过程。
可以使用setApplicationStartup设置一个实现ApplicationStartup的实例,比如,使用BufferingApplicationStartup的示例:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
}第一个可用的实现FlightRecorderApplicationStartup由 Spring Framework 提供。它将特定于 Spring 的启动事件添加到 Java Flight Recorder 会话,旨在分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GC、类加载……)相关联。配置完成后,您可以在启用飞行记录器的情况下运行应用程序来记录数据:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jarSpring Boot 附带BufferingApplicationStartup变体;此实现旨在缓冲启动步骤并将它们排入外部指标系统。BufferingApplicationStartup应用程序可以在任何组件中请求类型的 bean 。
Spring Boot 还可以配置为公开一个以 JSON 文档形式提供此信息的startup端点。
Spring Boot 允许您外部化您的配置,以便您可以在不同的环境中使用相同的应用程序代码。您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过注解直接注入 bean @Value,通过 Spring 的抽象Environment访问,或者通过@ConfigurationProperties绑定到对象。
Spring Boot 使用一种非常特殊的PropertySource顺序,旨在允许合理地覆盖值。后面的属性源可以覆盖前面定义的值。来源按以下顺序考虑:
SpringApplication.setDefaultProperties指定@Configuration上的@PropertySource注解,请注意,在刷新应用程序上下文之前,这些属性源不会添加到Environment中。而logging.* 和 spring.main.* 是在应用程序上下文刷新之前读取。application.propertiesRandomValuePropertySource中仅为random.*的配置java:comp/env的JNDI属性ServletContext初始化参数ServletConfig初始化参数SPRING_APPLICATION_JSON的属性,嵌入在环境变量(environment variable )或系统属性(system property)中的内联 JSONproperties,在@SpringBootTest 和用于测试应用程序特定部分的测试注解上有效。@TestPropertySource$HOME/.config/spring-boot目录中的Devtools全局设置配置Config 数据的加载按照以下顺序:
application.properties 和 YAML变体application-{profile}.properties和 YAML 变体application.properties和 YAML 变体application-{profile}.properties和 YAML 变体建议使用一种配置文件格式,如果同时有properties和yaml,properties优先。
假设您开发了一个@Component使用name属性的应用程序,如以下示例所示:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}在您的应用程序类路径中(例如,在您的 jar 中),您可以有一个application.properties文件为name. 在新环境中运行时,application.properties可以在 jar 之外提供一个文件来覆盖name. 对于一次性测试,您可以使用特定的命令行开关(例如,java -jar app.jar --name="Spring")启动。
env和configprops端点可用于确定属性具有特定值的原因。可以使用这两个端点来诊断预期外的属性值。有关详细信息,请参阅"Production ready features"部分。
默认情况下,SpringApplication将任何命令行选项参数(即以 --开头的参数,例如--server.port=9000)转换为property并将它们添加到 SpringEnvironment中,命令行属性始终优先于基于文件的属性源。
如果您不想将命令行属性添加到 中Environment,您可以使用SpringApplication.setAddCommandLineProperties(false)禁用它们。
环境变量和系统属性通常有限制,这意味着某些属性名称不能使用。为了解决这个问题,Spring Boot 允许您将属性块编码为单个 JSON 结构。
当您的应用程序启动时,任何spring.application.json或SPRING_APPLICATION_JSON属性将被解析并添加到Environment.
例如,SPRING_APPLICATION_JSON可以在 UN*X shell 的命令行中将属性作为环境变量提供:
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar在前面的示例中,您最终在 Spring 的Environment中获取my.name=test。
同样的 JSON 也可以作为系统属性提供:
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar或者您可以使用命令行参数提供 JSON:
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'如果要部署到经典应用程序服务器,您还可以使用名为java:comp/env/spring.application.json的JNDI 变量。
虽然JSON中的null将添加到结果属性源中,但PropertySourcesPropertyResolver会将null属性视为缺少的值。这意味着JSON不能用null值覆盖低优先级属性源中的属性。
当您的应用程序启动时,Spring Boot 将自动从以下位置查找并加载application.properties和application.yaml
这个列表按顺序排列(较低的项会覆盖较早的项)。加载的文件会做为PropertySources添加到Spring Environment中。
如果您不想用application作为配置文件名,您可以通过指定一个spring.config.name环境属性来切换到另一个文件名。例如,要查找myproject.properties和myproject.yaml文件,您可以按如下方式运行您的应用程序:
$ java -jar myproject.jar --spring.config.name=myproject您还可以使用spring.config.location环境属性来引用显式位置。此属性接受一个或多个要检查的位置的逗号分隔列表。
以下示例显示如何指定两个不同的文件:
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
optional前缀表示,位置是可选的,允许不存在
spring.config.name,spring.config.location, 和spring.config.additional-location很早就用于确定必须加载哪些文件。它们必须定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。
如果spring.config.location包含目录(而不是文件),应该以/结尾。在运行时,它们将在加载之前附加从spring.config.name生成的名称
目录和文件定位也用于 检查 profile指定文件。例如,如果
spring.config.location配置为classpath:myconfig.properties,classpath:myconfig-<profile>.properties的文件也会被加载
在大多数情况下,spring.config.location您添加的每个项目都将引用一个文件或目录。位置按照它们被定义的顺序处理,后面的可以覆盖前面的值。
如果您有一个复杂的位置要设置,并且您使用profile指定的配置文件,那么您可能需要提供进一步的提示,以便Spring Boot知道它们应该如何分组。位置组是所有被认为处于同一级别的位置的集合。例如,您可能希望对所有类路径位置进行分组,然后对所有外部位置进行分组。位置组中的项目用;分隔,详细查看Profile Specific Files
使用spring.config.location替换默认的位置配置。例如,如果spring.config.location设置为optional:classpath:/custom-config/,optional:file:./custom-config/,则完整的位置集是:
optional:classpath:custom-config/optional:file:./custom-config/如果您更喜欢添加其他位置,而不是替换它们,您可以使用spring.config.additional-location. 从其他位置加载的属性可以覆盖默认位置中的属性。
例如,如果spring.config.additional-location配置了值optional:classpath:/custom-config/,optional:file:./custom-config/,则考虑的完整位置集是:
optional:classpath:/;optional:classpath:/config/optional:file:./;optional:file:./config/;optional:file:./config/*/optional:classpath:custom-config/optional:file:./custom-config/此搜索顺序使您可以在一个配置文件中指定默认值,然后在另一个配置文件中有选择地覆盖这些值。您可以在默认位置之一的application.properties(或您选择的任何其他基本名称spring.config.name)中为您的应用程序提供默认值。然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。
如果您使用环境变量而不是系统属性,大多数操作系统不允许使用句点分隔的键名,但您可以使用下划线代替(例如,
SPRING_CONFIG_NAME代替spring.config.name)。有关详细信息,请参阅从环境变量绑定。如果您的应用程序在 servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在
java:comp/env中)或 servlet 上下文初始化参数来代替或同时使用环境变量或系统属性。
默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出ConfigDataLocationNotFoundException,并且应用程序将停止。
如果需要指定一个位置,但不是必须存在,使用optional:前缀。可以在spring.config.location和spring.config.additional-location以及spring.config.import中声明。
比如,spring.config.import属性,值为optional:file:./myconfig.properties,在文件不存在的情况下,应用程序也能够启动。
如果你想要忽略所有的ConfigDataLocationNotFoundExceptions异常,并且始终允许应用程序继续启动,可以使用spring.config.on-not-found配置。或者通过SpringApplication.setDefaultProperties(…)或者使用系统/环境变量设置忽略的值。
如果一个配置文件位置路径最后包含*,则表示其为通配符位置。这在多个配置文件的情况下,非常有用。
比如,有一些Redis配置和Mysql配置,可以想要把这两个配置文件分开,但又在application.properties文件中,这样可能会有两个不同的路径,/config/redis/application.properties 和/config/mysql/application.properties,通过config/*/可以将两个配置文件都进行加载。
默认情况下,Spring Boot在默认搜索位置包含config/*/,这意味着将搜索jar之外的/config目录的所有子目录。
您可以将通配符与spring.config.location和spring.config.additional-location一起使用。
通配符位置定位只能包含一个
*,对于搜索目录必须以*/结尾,对于搜索文件,则必须以*/<filename>结尾。带有通配符的位置根据文件名的绝对路径按字母顺序排序。通配符位置仅适用于外部目录。不能在
classpath:location中使用通配符。
除了application属性文件之外,Spring Boot还将尝试使用命名约定application-{profile}加载profile特定文件。例如,如果应用程序激活名为prod的配置文件并使用YAML文件,那么将同时加载application.yml和application-prod.yml。
Profile特定文件的属性加载与标准应用程序属性加载的位置相同,profile特定文件总是覆盖非特定的文件(application.yml)。如果指定了多个配置文件,则采用最后获胜策略。例如,如果配置文件 prod 、live 是由spring.profiles.active属性指定的,那么application-prod.properties中的值可以被application-live.properties中的值覆盖。
最后获胜策略适用于位置组级别。spring.config.location的classpath:/cfg/,classpath:/ext/配置和classpath:/cfg/;classpath:/ext/配置的覆盖规则不同。
例如,继续上面的prod、live示例,我们可能有以下文件:
/cfg
application-live.properties
/ext
application-live.properties
application-prod.propertiesspring.config.location的值为classpath:/cfg/,classpath:/ext/,程序会先处理/cfg下的所有文件,再处理/ext
/cfg/application-live.properties/ext/application-prod.properties/ext/application-live.properties如果值为classpath:/cfg/;classpath:/ext/,程序视为同一级别
/ext/application-prod.properties/cfg/application-live.properties/ext/application-live.propertiesEnvironment有一组默认配置文件(默认情况下为[default]),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,那么将考虑application-default。
配置文件只加载一次。如果您已经直接导入了特定配置文件的属性文件,则不会再次导入该文件。
应用程序配置可以使用spring.config.import 属性从其他位置导入更多配置数据。
例如,classpath application.properties文件中可能包含以下内容:
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties这将触发当前目录中dev.properties文件的导入(如果存在这样的文件)。导入的dev.properties中的值将优先于触发导入的文件。在上面的示例中,dev.properties可以将spring.application.name重新定义为不同的值。
无论声明多少次,都只能导入一次。在导入properties/yaml的文件中定义的单个文档顺序是无关紧要的,比如,下面的两个例子产生相同的结果。
spring.config.import=my.properties
my.property=valuemy.property=value
spring.config.import=my.properties在上述两个示例中,my.properties文件中的值将优先于触发其导入的文件。可以在一个spring.config.import下指定多个位置,位置将按照定义的顺序进行处理,以后导入的配置优先。
适当时,特定配置文件的变体还会导入,上面的示例将导入
my.properties以及任何my-<profile>.properties变体。Spring Boot包括可插拔API,允许支持各种不同的位置地址。默认情况下,您可以导入Java配置、YAML和“配置树”。 第三方jar可以提供对其他技术的支持(不要求文件是本地的)。例如,您可以想象配置数据来自Consul、Apache ZooKeeper或Netflix Archaius等外部存储。 如果要支持自定义位置,请参阅
org.springframework.boot.context.config包中的ConfigDataLocationResolver和ConfigDataLoader类。
某些云平台无法向卷装载的文件添加文件扩展名。要导入这些无扩展名文件,您需要给Spring Boot一个提示,以便它知道如何加载它们。您可以通过在方括号中放置扩展提示来完成此操作。
例如,假设您有一个/etc/config/myconfig文件,希望将其作为yaml导入。您可以使用以下命令从application.properties导入它:
spring.config.import=file:/etc/config/myconfig[.yaml]在云平台(如Kubernetes)上运行应用程序时,通常需要读取平台提供的配置值。出于这种目的使用环境变量并不罕见,但这可能会有缺点,特别是如果值应该保密的话。
作为环境变量的替代方案,许多云平台现在允许您将配置映射到装载的数据卷中。例如,Kubernetes可以卷装载ConfigMaps和Secrets。
可以使用两种常见的卷装载模式:
对于第一种情况,可以上述配置使用spring.config.import导入YAML或Properties文件。
对于第二种情况,您需要使用configtree:前缀,以便Spring Boot知道它需要将所有文件公开为Properties。
例如,让我们假设Kubernetes安装了以下卷:
etc/
config/
myapp/
username
passwordusername 是一个配置的值,password是一个加密字符串
要导入这些配置,你可以将如下内容导入application.properties或者application.yaml
spring.config.import=optional:configtree:/etc/config/然后,您可以用通常的方式从Environment中访问或注入myapp.username和myapp.password属性。
📌 配置树下的文件夹构成属性名称。在上面的示例中,要以
username和password的形式访问属性,可以将spring.config.import设置为optional:configtree:/etc/config/myapp。带有点符号的文件名也正确映射。例如,在上面的示例中,
/etc/config中名为myapp.username的文件将在Environment中生成myapp.username属性。📌 配置树值可以绑定到字符串
String和byte[]类型,具体取决于预期的内容。
如果要从同一父文件夹导入多个配置树,则可以使用通配符快捷方式。任何以/*/结尾的configtree:location都会将所有直接子级作为配置树导入。
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password您可以使用configtree:/etc/config/*/作为导入位置:
spring.config.import=optional:configtree:/etc/config/*/如上配置将导入 db.username, db.password, mq.username 和 mq.password 属性。
🚩使用通配符加载的目录按字母顺序排序。如果您需要不同的排序,则应将每个位置列为单独的导入
配置树也可以用于Docker 保密数据。当Docker群服务被授权访问一个保密数据时,该保密数据被装入容器中。例如,如果名为db.password的保密数据安装在位置/run/secrets/,则可以使用以下变量db.passwords在Spring环境中:
spring.config.import=optional:configtree:/run/secrets/application.properties和application.yml中的值在使用时会通过现有的Environment进行过滤,因此您可以引用以前定义的值(例如,从系统属性或环境变量)。标准的{name}属性占位符语法可以在值的任何位置使用,属性占位符还可以使用:指定默认值,将默认值与属性名称分开,例如{name:default}。
以下示例显示了带默认值和不带默认值的占位符的使用:
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}您应该始终使用占位符中的规范形式(kebab-case仅使用小写字母)引用占位符中的属性名称。这将允许Spring Boot使用与
@ConfigurationProperties相同的宽松绑定逻辑。 例如,{demo.item-price}将从application.properties文件中获取demo.iterm-price和demo.itemPrice,并从系统环境中获取DEMO_ITEMPRICE。如果改用{demo.itemPrice},则不会考虑demo.item-price和DEMO_ITEMPRICE。🚩您还可以使用此技术创建现有SpringBoot属性的“短”变体。有关详细信息,请参阅使用“短”命令行参数的方法。
Spring Boot允许您将单个物理文件拆分为多个逻辑文档,每个逻辑文档都是独立添加的。文档按照从上到下的顺序进行处理。后续文档可以覆盖早期文档中定义的配置。
对于application.yml文件,使用标准的YAML多文档语法。三个连续的连字符表示一个文档的结尾和下一个文档开始。
例如,以下包含两个逻辑文档:
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"application.properties文件使用#---或者!--- 来分割文档
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes🚩 配置文件分隔符不能有任何前导空格,必须正好有三个连字符。
📌 多文档属性文件通常与激活配置(如
spring.config.activate.on-profile)结合使用。有关详细信息,请参阅下一节。 无法使用@PropertySource或@TestPropertySource注解加载多文档属性文件。
您可能具有仅在特定配置文件处于激活状态时才关联配置。
您可以使用spring.config.activate.*有条件地激活配置属性。
下面的激活配置可用:
Property | Note |
|---|---|
on-profile | 必须匹配才能激活文档的配置文件表达式 |
on-cloud-platform | 要使文档处于活动状态,必须检测到“CloudPlatform” |
例如,下面指定第二个文档仅在Kubernetes上运行时有效,并且仅在“prod”或“staging”配置文件处于激活状态时有效:
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-setSpring Boot不提供任何加密属性的内置支持,但它提供了修改Spring环境中包含值所需的钩子点。EnvironmentPostProcessor允许你在应用程序启动的时候控制Environment,详细查看[启动时自定义环境变量](#15. “How-to” 指南)。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault项目将支持在HashiCorp Vault中存储外部化配置。
YAML是JSON的超集,是指定分层配置数据的便捷格式。只要类路径上有SnakeYAML库,SpringApplication类就会自动支持YAML作为properties的替代。
YAML文档需要从其分层格式转换为可用于Spring Environment的平面结构。
例如,如下的YAML文档:
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"为了从Environment中访问这些属性,它们将按以下方式展平:
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列表也需要扁平化,使用[index]做为键,比如下面的YAML。
my:
servers:
- "dev.example.com"
- "another.example.com"上述示例转为properties后:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com使用[index]表示的properties能够绑定到Java的List或Set对象。有关更多详细信息,请参阅下面的“[类型安全配置属性](#5.2.8 类型安全配置属性)”部分。
无法使用@PropertySource或@TestPropertySource注解加载YAML文件。因此,如果需要以这种方式加载值,则需要使用properties文件。
Spring Framework提供了两个方便类,可用于加载YAML文档。YamlPropertiesFactoryBean将YAML作为Properties加载,YamlMapFactoryBean将YAML作为Map加载。
如果要将YAML作为Spring PropertySource加载,也可以使用YamlPropertySourceLoader类。
RandomValuePropertySource用于注入随机值(例如,注入加密字符或测试用例)。它可以生成integer、long、uuid或string,如下例所示:
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是最大值(不包括)。
Spring Boot支持为环境属性设置前缀。如果系统环境由具有不同配置要求的多个Spring Boot应用程序共享,这将非常有用。系统环境属性的前缀可以直接在SpringApplication上设置。
例如,如果将前缀设置为input,则诸如remote.timeout之类的属性也将在系统环境中解析为input.remote.timeout。
使用@Value("${property}")注入配置属性有时会很麻烦,特别是当您有多个属性或数据本质上是分层的时候。SpringBoot提供了另一种使用properties的方法,该方法允许强类型bean管理和验证应用程序的配置。
也可以查看@Value 和 type-safe configuration properties的不同点
可以绑定到一个标准的JavaBean,如下例所示:
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getRoles() {
return this.roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
}前面的POJO定义了以下属性:
String映射到Spring Boot中可用的@ConfigurationProperties类的properties是公共API,这些类是通过properties文件、YAML文件、环境变量和其他机制配置的,但类本身的访问器(getters/setters)并不打算直接使用。
这种安排依赖于默认的空构造函数,getter和setter通常是强制性的,因为绑定是通过标准的JavaBeans属性描述符进行的,就像在SpringMVC中一样。在下列情况下,可以省略setter:
Security前面示例中的字段),则不需要 setter。如果希望绑定器使用其默认构造函数动态创建实例,则需要setter。有些人使用 Project Lombok 来自动添加 getter 和 setter。确保 Lombok 不会为此类类型生成任何特殊的构造函数,因为容器会自动使用它来实例化对象。 最后,只考虑标准 Java Bean 属性,不支持绑定静态属性。
上一节中的示例可以以不可变的方式重写,如以下示例所示:
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List<String> getRoles() {
return this.roles;
}
}
}在此设置中,@ConstructorBinding注解用于指示应使用构造函数绑定。这意味着绑定器将期望找到一个带有您希望绑定的参数的构造函数。如果您使用的是 Java 16 或更高版本,构造函数绑定可以与记录一起使用。在这种情况下,除非您的记录有多个构造函数,否则没有必要使用@ConstructorBinding.
类的嵌套成员@ConstructorBinding(如上Security例)也将通过其构造函数进行绑定。
可以使用@DefaultValue构造函数参数指定默认值,或者在使用 Java 16 或更高版本时使用记录组件指定默认值。转换服务将用于将String值强制转换为缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到Security,则该MyProperties实例将包含 一个null值的security。要使它包含一个非空的实例,Security即使没有属性绑定到它(使用 Kotlin 时,这将需要将 的username和password参数Security声明为可空的,因为它们没有默认值),使用空@DefaultValue注解:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}🚩 要使用构造函数绑定,必须使用
@EnableConfigurationProperties或@ConfigurationProperties来启用类。您不能对由常规 Spring 机制创建的 bean 使用构造函数绑定(例如@Componentbean、使用@Bean方法创建的 bean 或使用@Import加载的 bean)📌 如果您的类有多个构造函数,您也可以在应该绑定的构造函数上直接使用@ConstructorBinding
🚩 不建议将
java.util.Optional与@ConfigurationProperties一起使用,因为它主要用作返回类型。因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果您确实声明了一个Optional属性并且它没有值,那么将绑定null一个空值。
Spring Boot 提供基础设施来绑定@ConfigurationProperties类型并将它们注册为 beans。您可以逐个类地启用配置属性,也可以启用以类似于组件扫描的方式工作的配置属性扫描。
有时,带有注解的类@ConfigurationProperties可能不适合扫描,例如,如果您正在开发自己的自动配置或您希望有条件地启用它们。在这些情况下,使用@EnableConfigurationProperties注解指定要处理的类型列表。这可以在任何@Configuration类上完成,如以下示例所示:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}要使用配置属性扫描,请将@ConfigurationPropertiesScan注解添加到您的应用程序。通常,它被添加到带有@SpringBootApplication的类中,但它可以添加到任何@Configuration类中。默认情况下,将从声明注解的类的包中进行扫描。如果要定义要扫描的指定包,可以按照以下示例所示进行操作:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}当
@ConfigurationProperties使用配置属性扫描或通过@EnableConfigurationProperties注册 bean时,bean 有一个约定名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties中指定的环境键前缀,<fqn>是 bean 的完全限定名称。如果不提供任何前缀,则仅使用 bean 的完全限定名称。 上面示例中的 bean 名称是com.example.app-com.example.app.SomeProperties.
我们建议@ConfigurationProperties只处理环境,特别是不要从上下文中注入其他 beans。对于极端情况,可以使用 setter 注入或*Aware框架提供的任何接口(例如,EnvironmentAware如果您需要访问Environment)。如果您仍想使用构造函数注入其他 bean,则配置属性 bean 必须注释@Component并使用基于 JavaBean 的属性绑定。
这种类型的配置在SpringApplication外部YAML配置中尤其适用,如下例所示:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"要使用@ConfigurationProperties bean,可以与任何其他bean相同的方式注入,如下例所示:
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}使用@ConfigurationProperties还可以生成元数据文件,IDE可以使用这些文件自动完成自己的密钥。详见附录。
除了使用@ConfigurationProperties注解类之外,还可以在公共@Bean方法上使用它。当您想将属性绑定到不在您控制范围内的第三方组件时,这样做特别有用。
要从Environment中配置bean,请将@ConfigurationProperties添加到其bean注册中,如以下示例所示:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}使用another前缀定义的属性都以类似于前面的SomeProperties示例的方式映射到该AnotherComponent bean上。
Spring Boot使用一些宽松的规则将Environment属性绑定到@ConfigurationProperties bean,因此,Environment属性名称和bean属性名称之间不需要完全匹配。这很有用的常见示例包括以破折号分隔的环境属性(例如,context-path绑定到contextPath),和大写的环境属性(例如,PORT绑定到port)。
例如,以下@ConfigurationProperties类:
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}使用上述代码,可以使用以下属性名称:
Property | Note |
|---|---|
my.main-project.person.first-name | 建议在.properties和.yml文件中使用 |
my.main-project.person.firstName | 标准的驼峰写法 |
my.main-project.person.first_name | 下划线表示法,建议在.properties和.yml文件中使用 |
MY_MAINPROJECT_PERSON_FIRSTNAME | 大写格式,建议在系统环境变量中使用 |
注解的前缀值必须是kebab大小写(小写并用
-分隔,例如my.main-project.person)。
Property Source | Simple | List |
|---|---|---|
Properties Files | 驼峰大小写、kebab小写或下划线符号 | 标准的使用[ ]或者逗号分割值 |
YAML Files | 驼峰大小写、kebab小写或下划线符号 | 标准的YAML列表或者逗号分割值 |
Environment Variables | 以下划线作为分隔符的大写格式( Binding From Environment Variables). | 用下划线包围的数值 (see Binding From Environment Variables) |
System properties | 驼峰大小写、kebab小写或下划线符号 | 标准的使用[ ]或者逗号分割值 |
我们建议在可能的情况下,将属性存储为小写的kebab格式,例如my.person.first-name=Rod。
绑定到Map配置时,可能需要使用特殊的括号表示法,以便保留原始键值。如果键未被[]包围,则为非字母数字、-或.任何字符将被移除。
例如,以下示例绑定到Map<String,String>:
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3对于YAML文件,括号需要用引号括起来,以便正确解析键。
上面的配置将以/key1、/key2和key3作为映射中的键绑定到Map。斜线已从key3中删除,因为它没有被方括号包围。
当绑定到标量值时,使用键.其中不需要被[]包围。标量值包括枚举和java.lang包中除Object之外的所有类型。将a.b=c绑定到Map<String, String>将会保留.,并返回包含{"a.b"="c"}项的map。对于任何其他类型,如果键包含.,则需要使用括号表示法。比如,将a.b=c绑定到Map<String, Object>,将返回{"a"={"b"="c"}}项的map,而[a.b]=c将返回{"a.b"="c"}项的map。
大多数操作系统对可用于环境变量的名称施加严格的规则。例如,Linux shell变量只能包含字母(a到z或a到z)、数字(0到9)或下划线字符(_)。按照惯例,Unix shell变量的名称也将以大写字母表示。
Spring Boot的宽松绑定规则尽可能与这些命名限制兼容。
要将规范形式的属性名称转换为环境变量名称,可以遵循以下规则:
.替换为_-例如,一个spring.main.log-startup-info属性转换为环境变量后为SPRING_MAIN_LOGSTARTUPINFO。
绑定到对象列表时也可以使用环境变量。要绑定到List,元素编号应在变量名称中用下划线包围。
例如,一个my.service[0].other转换为环境变量后是MY_SERVICE_0_OTHER。
当在多个位置配置列表时,覆盖通过替换整个列表来工作。例如,假设MyPojo对象的名称和描述属性默认为null。以下示例显示MyProperties中的MyPojo对象列表:
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}可以如下配置:
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name如果dev未激活,MyProperties.list包含一个MyPojo项,如果dev激活,然而,列表仍然只包含一个条目(名称为my another name,description为null)。此配置不会向列表中添加第二个MyPojo实例,也不会合并项目。
当在多个配置文件中指定列表时,将使用优先级最高的配置文件(并且仅使用该配置文件)。
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name在前面的示例中,dev激活,MyProperties.list包含一个MyPojo项,name为my another name和description为null。对于YAML,逗号分隔列表和YAML列表都可以用于完全覆盖列表的内容。
对于Map属性,可以使用从多个源绘制的属性值进行绑定。但是,对于多个源中的相同属性,将使用具有最高优先级的属性。以下示例,MyProperties公开了一个Map<String, MyPojo>属性。
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}以下示例配置:
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2如果dev未激活,MyProperties.map仅包含一个key为key1的项(name是my name 1,description是my description 1)。如果dev激活,将包含两个项目键为key1(name是dev name 1,description是my description 1),键为key2(name是dev name 2,description是my description 2)
上述合并规则适用于所有属性源的配置,而不仅仅是文件。
当绑定到@ConfigurationProperties bean时,SpringBoot会尝试将外部应用程序属性强制为正确的类型。如果需要自定义类型转换,可以提供ConversionService bean(带有名为conversionService的bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或定制Converter(带有@ConfigurationPropertiesBinding注解的bean定义)。
由于此bean在应用程序生命周期的早期被请求,请确保限制
ConversionService正在使用的依赖关系。通常,您需要的任何依赖项在创建时都可能无法完全初始化。如果配置键不强制需要,并且仅依赖于用@ConfigurationPropertiesBinding限定的自定义转换器,则可能需要重命名自定义ConversionService。
Spring Boot 支持Durations,如果你公开java.time.Duration,应用程序中可以用以下格式:
long描述,如果没有指定@DurationUnit,默认是毫秒java.time.Duration使用的标准ISO-8601格式import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
@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;
}
}指定会话超时时间30s,PT30S和30s等效,读取超时500ms可以以下列任意形式指定:500、PT0.5S和500ms。
您也可以使用任何支持的单位:
ns 纳秒us 微秒ms 毫秒s 秒m 分h 小时d 天默认单位为毫秒,可以使用@DurationUnit重写,如上面的示例所示。
如果您喜欢使用构造函数绑定,可以公开相同的属性,如以下示例所示:
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
private final Duration sessionTimeout;
private final Duration readTimeout;
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}如果要升级Long属性,请确保定义单位(使用@DurationUnit)(如果不是毫秒)。这样做可以提供透明的升级路径,同时支持更丰富的格式。
除了持续时间,Spring Boot还可以使用java.time.Period类型。应用程序配置中可以使用以下格式:
int描述,默认使用天,除非指定了@PeriodUnitjava.time.Period使用标准的ISO-8601简单格式支持以下单位:
y 年m 月w 周d 天java.time.Period类型实际上从未存储周数,它是一个表示“7天”的快捷方式。
Spring Framework具有以字节表示大小的DataSize值类型,如果你要公开DataSize,以下格式可以使用:
long格式,默认使用bytes,除非指定了@DataSizeUnit如下示例:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
@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。您也可以使用任何支持的单位。这些是:
B bytesKB kilobytesMB megabytesGB gigabytesTB terabytes默认单位是字节,可以使用@DataSizeUnit重写,如上面的示例所示。
如果您喜欢使用构造函数绑定,可以公开相同的属性,如以下示例所示:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
private final DataSize bufferSize;
private final DataSize sizeThreshold;
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}如果要升级Long属性,请确保定义单位(使用@DataSizeUnit)(如果不是字节)。这样做可以提供透明的升级路径,同时支持更丰富的格式。
当@ConfigurationProperties类被Spring的@Validated注解注释时,Spring Boot会尝试验证它们。您可以直接在配置类上使用JSR-303 javax.validation约束注释。要做到这一点,请确保类路径上有一个兼容的JSR-303实现,然后向字段添加约束注解,如下例所示:
import java.net.InetAddress;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
}您还可以通过注释@Bean方法来触发验证,该方法使用@Validated创建配置属性。
为了确保始终为嵌套属性触发验证,即使找不到,也必须用@Valid注解相关字段。以下示例基于前面的MyProperties示例:
import java.net.InetAddress;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
@NotEmpty
private String username;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
}您还可以通过创建名为configurationPropertiesValidator的bean定义来添加自定义Spring Validator。@Bean方法应声明为静态。配置属性验证器是在应用程序生命周期的早期创建的,将@Bean方法声明为static创建Bean,而无需实例化@configuration类。这样做可以避免早期实例化可能导致的任何问题。
spring-boot-actuator包括一个端点,它公开所有
@ConfigurationPropertiesbean。将web浏览器指向/actuator/configprops或使用等效的JMX端点。有关详细信息,请参阅“[生产就绪功能](#11. 生产就绪功能)”部分。
@Value注解是一个核心容器功能,它提供的功能与类型安全配置属性不同。下表总结了@ConfigurationProperties和@Value支持的功能:
Feature | @ConfigurationProperties | @Value |
|---|---|---|
宽松绑定 | Yes | Limited (see note below) |
元数据支持 | Yes | No |
SpEL 表达式 | No | Yes |
如果您确实想使用
@Value,我们建议您使用规范形式引用属性名称(kebab-case仅使用小写字母)。这将允许Spring Boot与使用宽松绑定的@ConfigurationProperties相同的逻辑。 例如,@Value(“{demo.item-price}”)将从application.properties文件中获取demo.iitem-price和demo.itermPrice表单,并从系统环境中获取DEMO_ITEMPRICE。如果改用@Value(“{demo.itemPrice}”),则不会考虑demo.item-price和DEMO_ITEMPRICE。
如果您为自己的组件定义了一组配置键,我们建议您将它们分组到带有@ConfigurationProperties注释的POJO中。这样做将为您提供结构化的类型安全对象,您可以将其注入到自己的bean中。
在解析这些文件并填充环境时,不会处理应用程序属性文件中的SpEL表达式。但是,可以在@Value中编写SpEL表达式。如果应用程序属性文件中的属性值是SpEL表达式,则在通过@value使用时将对其求值。
Spring profiels 提供了一种隔离应用程序配置部分的方法,使其仅在特定环境中可用。任何@Component、@Configuration或@ConfigurationProperties都可以用@Profile标记加以限制,如下例所示:
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}如果@ConfigurationProperties bean是通过@EnableConfigurationProperties而不是自动扫描注册的,则需要在具有@EnableConfigurationProperty注解的@Configuration类上指定@Profile注解。在扫描@ConfigurationProperties的情况下,可以在@ConfigurationProperties类本身上指定@Profile。
可以使用spring.profiles.active Environment属性来指定哪些配置文件处于活动状态。您可以用本章前面描述的任何方式指定属性,例如,您可以将其包含在application.properties中,如下例所示:
spring.profiles.active=dev,hsqldb也可以使用以下开关在命令行上指定它:--spring.profiles.active=dev,hsqldb。
如果没有激活配置文件,则启用默认配置文件。默认配置文件的名称是默认的,可以使用spring.profile.default Environment属性对其进行调整,如下例所示:
spring.profiles.default=nonespring.profiles.active和spring.profiles.default只能在非配置文件特定的文档中使用。这意味着它们不能包含在spring.config.activate.on-profile激活的特定配置文件的文件或激活属性中。
例如,如下配置无效:
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metricsspring.profiles.active属性遵循与其他属性相同的排序规则。PropertySource优先级最高,这意味着您可以在application.properties中指定活动配置文件,然后使用命令行开关替换它们。
有时,将配置添加到活动配置文件而不是替换它们是很有用的。spring.profiles.include属性可用于在spring.profiles.active属性激活的配置文件之上添加活动配置文件。
SpringApplication入口点还具有用于设置其他配置文件的Java API,请参阅SpringApplication中的setAdditionalProfiles()方法。
例如,当运行以下配置的应用程序时,即使使用-spring.profiles.active 开关运行,也会激活common和local配置文件:
spring.profiles.include[0]=common
spring.profiles.include[1]=local与spring.profile.active类似,
spring.profile.include只能用于非配置文件特定的文档。
如果给定的配置文件处于活动状态,则也可以使用配置文件组(在下一节中介绍)添加活动的配置文件。
有时,您在应用程序中定义和使用的配置文件过于细粒度,使用起来很麻烦。例如,您可以使用proddb和prodmq配置文件来独立启用数据库和消息传递功能。
为了帮助实现这一点,Spring Boot允许您定义配置文件组。配置文件组允许您定义相关配置文件组的逻辑名称。
例如,我们可以创建一个由prodb和prodmq配置文件组成的生产组。
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq我们的应用程序现在可以使用--spring.profiles.active=production启动,一次激活production、proddb和prodmq配置文件。
您可以在应用程序运行之前通过调用SpringApplication.setAdditionalProfiles(...),还可以使用Spring的ConfigurationEnvironment接口激活配置文件。
application.properties(或application.yml)和通过@ConfigurationProperties引用的文件的特定配置文件的变体都被视为文件并被加载。有关详细信息,请参阅“Profile特定文件”。
后续章节未完成,待补充