Appearance
Spring Boot:核心特性 - Part1
本节深入探讨了 Spring Boot 的细节。您可以在这里学习到您可能想要使用和自定义的关键特性。如果您还没有这样做,您可能需要阅读 “入门” 和 “使用 Spring Boot 进行开发” 部分,以便您对基础知识有一个良好的理解。
1. SpringApplication
SpringApplication 类提供了一种方便的方式来启动从 main() 方法启动的 Spring 应用程序。在许多情况下,您可以委托给静态 SpringApplication.run 方法,如下例所示:
Java
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);
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
当您的应用程序启动时,您应该看到类似以下输出的内容:
Text
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.18)
2023-11-23 07:23:23.238 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28579 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23 07:23:23.245 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default"
2023-11-23 07:23:24.552 INFO 28579 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-11-23 07:23:24.567 INFO 28579 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-11-23 07:23:24.567 INFO 28579 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.83]
2023-11-23 07:23:24.658 INFO 28579 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-11-23 07:23:24.659 INFO 28579 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1346 ms
2023-11-23 07:23:25.336 INFO 28579 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-23 07:23:25.355 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.63 seconds (JVM running for 3.003)默认情况下,显示 INFO 级别的日志消息,包括一些相关的启动细节,例如启动应用程序的用户。如果需要其他日志级别,可以按照日志级别中的描述进行设置。应用程序版本是使用主应用程序类的包的实现版本确定的。可以通过将 spring.main.log-startup-info 设置为 false 来关闭启动信息日志记录。这也会关闭应用程序活动配置文件的日志记录。
Note:要在启动期间添加额外的日志记录,可以在
SpringApplication的子类中覆盖logStartupInfo(boolean)。
1.1. 启动失败
如果您的应用程序启动失败,注册的 FailureAnalyzers 会有机会提供一个专用的错误消息和一个具体的操作来修复问题。例如,如果您在端口 8080 上启动 Web 应用程序,并且该端口已被使用,您应该会看到类似以下消息的内容:
Text
***************************
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.Tip:Spring Boot 提供了许多
FailureAnalyzer实现,您还可以添加自己的。
如果没有失败分析器能够处理异常,您仍然可以显示完整的条件报告以更好地了解问题所在。要做到这一点,您需要启用 debug 属性或启用 DEBUG 日志记录,以便 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener。
例如,如果您使用 java -jar 运行应用程序,可以通过以下方式启用 debug 属性:
Bash
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug1.2. 懒加载初始化
SpringApplication 允许应用程序进行懒加载初始化。启用懒加载初始化时,bean 在需要时创建,而不是在应用程序启动时创建。因此,启用懒加载初始化可以减少应用程序启动所需的时间。在 Web 应用程序中,启用懒加载初始化将导致许多 Web 相关的 bean 在接收到 HTTP 请求之前不会被初始化。
懒加载初始化的一个缺点是它可能会延迟发现应用程序的问题。如果一个配置错误的 bean 被懒加载初始化,失败将不再发生在启动期间,问题只会在初始化 bean 时变得明显。还必须确保 JVM 有足够的内存来容纳应用程序的所有 bean,而不仅仅是在启动期间初始化的 bean。由于这些原因,默认情况下不启用懒加载初始化,建议在启用懒加载初始化之前对 JVM 的堆大小进行微调。
可以使用 SpringApplicationBuilder 的 lazyInitialization 方法或 SpringApplication 的 setLazyInitialization 方法以编程方式启用懒加载初始化。或者,可以使用 spring.main.lazy-initialization 属性启用懒加载初始化,如下例所示:
Properties
spring.main.lazy-initialization=trueNote:如果您希望在使用懒加载初始化的同时为某些 bean 禁用懒加载初始化,可以使用
@Lazy(false)注解显式地将它们的lazy属性设置为false。
1.3. 自定义 Banner
启动时打印的 Banner 可以通过将 banner.txt 文件添加到类路径或将 spring.banner.location 属性设置为此类文件的位置来更改。如果文件的编码不是 UTF-8,可以设置 spring.banner.charset。除了文本文件,还可以将 banner.gif、banner.jpg 或 banner.png 图像文件添加到类路径,或设置 spring.banner.image.location 属性。图像将被转换为 ASCII 艺术表示并打印在任何文本 Banner 之上。
在 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.18。 |
${spring-boot.formatted-version} | 您正在使用的 Spring Boot 版本,格式化以供显示(用括号括起来并以 v 为前缀)。例如 (v2.7.18)。 |
${Ansi.NAME}(或 ${AnsiColor.NAME}、${AnsiBackground.NAME}、${AnsiStyle.NAME}) | 其中 NAME 是 ANSI 转义代码的名称。请参阅 AnsiPropertySource 以获取详细信息。 |
${application.title} | 您的应用程序的标题,如 MANIFEST.MF 中所声明的。例如 Implementation-Title: MyApp 被打印为 MyApp。 |
Note:如果您希望以编程方式生成 Banner,可以使用
SpringApplication.setBanner(…)方法。使用org.springframework.boot.Banner接口并实现您自己的printBanner()方法。
还可以使用 spring.main.banner-mode 属性来确定是否将 Banner 打印到 System.out(console)、发送到配置的日志记录器(log),或根本不生成(off)。
打印的 Banner 被注册为名为 springBootBanner 的单例 bean。
Tip
application.title、application.version和application.formatted-version属性仅在使用java -jar或java -cp与 Spring Boot 启动器时可用。如果您运行未打包的 jar 并使用java -cp <classpath> <mainclass>启动它,这些值将不会被解析。要使用
application.属性,请将您的应用程序作为打包的 jar 使用java -jar启动,或作为未打包的 jar 使用java org.springframework.boot.loader.JarLauncher启动。这将在构建类路径并启动您的应用程序之前初始化application.Banner 属性。
1.4. 自定义 SpringApplication
如果 SpringApplication 的默认设置不符合您的需求,您可以创建一个本地实例并对其进行自定义。例如,要关闭 Banner,可以编写以下代码:
Java
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);
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Tip:传递给
SpringApplication的构造函数参数是 Spring bean 的配置源。在大多数情况下,这些是对@Configuration类的引用,但它们也可以是对@Component类的直接引用。
还可以使用 application.properties 文件配置 SpringApplication。有关详细信息,请参阅外部化配置。
有关配置选项的完整列表,请参阅 SpringApplication Javadoc。
1.5. 流式的构建器 API
如果您需要构建 ApplicationContext 层次结构(多个具有父/子关系的上下文),或者如果您更喜欢使用 “流式” 的构建器 API,可以使用 SpringApplicationBuilder。
SpringApplicationBuilder 允许您链接多个方法调用,并包括 parent 和 child 方法,这些方法允许您创建层次结构,如下例所示:
Java
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);1
2
3
4
5
2
3
4
5
Tip:在创建
ApplicationContext层次结构时有一些限制。例如,Web 组件必须包含在子上下文中,并且父上下文和子上下文使用相同的Environment。有关完整详细信息,请参阅SpringApplicationBuilderJavadoc。
1.6. 应用程序可用性
在平台上部署应用程序时,应用程序可以使用基础设施(例如 Kubernetes Probes)提供有关其可用性的信息。Spring Boot 包括对常用的 “活动” 和 “就绪” 可用性状态的开箱即用支持。如果您使用 Spring Boot 的 “actuator” 支持,则这些状态作为健康端点组公开。
此外,您还可以通过将 ApplicationAvailability 接口注入到您自己的 bean 中来获取可用性状态。
1.6.1. 活动状态
应用程序的 “活动” 状态告知其内部状态是否允许其正常工作,或者在当前失败时是否可以自行恢复。破坏的 “活动” 状态意味着应用程序处于无法恢复的状态,基础设施应重新启动应用程序。
Tip:一般来说,“活动” 状态不应基于外部检查,例如健康检查。如果是这样,失败的外部系统(数据库、Web API、外部缓存)将触发大规模重新启动和平台上的级联故障。
Spring Boot 应用程序的内部状态主要由 Spring ApplicationContext 表示。如果应用程序上下文已成功启动,Spring Boot 假定应用程序处于有效状态。应用程序在上下文刷新后被认为是活动的,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
1.6.2. 就绪状态
应用程序的 “就绪” 状态告知应用程序是否准备好处理流量。失败的 “就绪” 状态告知平台目前不应将流量路由到应用程序。这通常发生在启动期间,当 CommandLineRunner 和 ApplicationRunner 组件被处理时,或者在任何时候,如果应用程序决定它对额外的流量太忙。
应用程序在调用应用程序和命令行运行器后被认为是就绪的,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
Note:预期在启动期间运行的任务应由
CommandLineRunner和ApplicationRunner组件执行,而不是使用 Spring 组件生命周期回调,例如@PostConstruct。
1.6.3. 管理应用程序可用性状态
应用程序组件可以随时检索当前可用性状态,通过注入 ApplicationAvailability 接口并调用其方法。更常见的是,应用程序希望侦听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的 “就绪” 状态导出到文件中,以便 Kubernetes “exec Probe” 可以查看该文件:
Java
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;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
我们还可以在应用程序崩溃并无法恢复时更新应用程序的状态:
Java
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);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Spring Boot 提供了 Kubernetes HTTP 探针,用于 “活动” 和 “就绪” 与 Actuator 健康端点。您可以在专门部分中获取有关在 Kubernetes 上部署 Spring Boot 应用程序的更多指导。
1.7. 应用程序事件和监听器
除了通常的 Spring Framework 事件(例如 ContextRefreshedEvent),SpringApplication 还会发送一些额外的应用程序事件。
Tip
某些事件实际上是在创建
ApplicationContext之前触发的,因此您无法将这些事件注册为@Bean。您可以使用SpringApplication.addListeners(…)方法或SpringApplicationBuilder.listeners(…)方法注册它们。如果您希望这些监听器自动注册,无论应用程序的创建方式如何,可以向项目添加
META-INF/spring.factories文件,并使用org.springframework.context.ApplicationListener键引用您的监听器,如下例所示:Propertiesorg.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件在应用程序运行时按以下顺序发送:
在任何处理之前(除了监听器和初始化器的注册)发送
ApplicationStartingEvent。在知道要在上下文中使用的
Environment但在创建上下文之前发送ApplicationEnvironmentPreparedEvent。在准备
ApplicationContext并调用ApplicationContextInitializers但在加载任何 bean 定义之前发送ApplicationContextInitializedEvent。在刷新开始之前但在加载 bean 定义之后发送
ApplicationPreparedEvent。在刷新上下文后但在调用任何应用程序和命令行运行器之前发送
ApplicationStartedEvent。随后发送带有
LivenessState.CORRECT的AvailabilityChangeEvent,以指示应用程序被认为是活动的。在调用任何应用程序和命令行运行器后发送
ApplicationReadyEvent。随后发送带有
ReadinessState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序准备好处理请求。如果启动时出现异常,则发送
ApplicationFailedEvent。
上述列表仅包括与 SpringApplication 绑定的 SpringApplicationEvent。除此之外,还会在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发布以下事件:
在
WebServer准备就绪后发送WebServerInitializedEvent。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是 Servlet 和响应式变体。在刷新
ApplicationContext时发送ContextRefreshedEvent。
Note:您通常不需要使用应用程序事件,但了解它们的存在可能会很方便。Spring Boot 内部使用事件来处理各种任务。
Tip:事件监听器不应运行可能耗时的任务,因为它们默认情况下在同一线程中执行。请考虑使用应用程序和命令行运行器。
应用程序事件使用 Spring Framework 的事件发布机制发送。该机制的一部分确保发布到子上下文中的监听器的事件也发布到祖先上下文中的监听器。因此,如果您的应用程序使用 SpringApplication 实例的层次结构,监听器可能会收到相同类型的应用程序事件的多个实例。
为了使您的监听器能够区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware 或(如果监听器是 bean)使用 @Autowired 来注入上下文。
1.8. Web 环境
SpringApplication 尝试为您创建正确类型的 ApplicationContext。用于确定 WebApplicationType 的算法如下:
如果存在 Spring MVC,则使用
AnnotationConfigServletWebServerApplicationContext如果不存在 Spring MVC 且存在 Spring WebFlux,则使用
AnnotationConfigReactiveWebServerApplicationContext否则,使用
AnnotationConfigApplicationContext
这意味着,如果您在同一应用程序中使用 Spring MVC 和新的 WebClient 从 Spring WebFlux,默认情况下将使用 Spring MVC。您可以通过调用 setWebApplicationType(WebApplicationType) 轻松覆盖它。
还可以通过调用 setApplicationContextFactory(…) 完全控制使用的 ApplicationContext 类型。
Note:在使用
SpringApplication进行 JUnit 测试时,通常希望调用setWebApplicationType(WebApplicationType.NONE)。
1.9. 访问应用程序参数
如果您需要访问传递给 SpringApplication.run(…) 的应用程序参数,可以注入 org.springframework.boot.ApplicationArguments bean。ApplicationArguments 接口提供对原始 String[] 参数以及解析的 option 和 non-option 参数的访问,如下例所示:
Java
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"]
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Note:Spring Boot 还会在 Spring
Environment中注册CommandLinePropertySource。这使您还可以使用@Value注解注入单个应用程序参数。
1.10. 使用 ApplicationRunner 或 CommandLineRunner
如果您需要在 SpringApplication 启动后运行一些特定的代码,可以实现 ApplicationRunner 或 CommandLineRunner 接口。这两个接口的工作方式相同,并提供一个 run 方法,该方法在 SpringApplication.run(…) 完成之前被调用。
Tip:此合同非常适合在应用程序启动后但在其开始接受流量之前运行的任务。
CommandLineRunner 接口提供对应用程序参数的访问,作为字符串数组,而 ApplicationRunner 使用前面讨论的 ApplicationArguments 接口。以下示例显示了具有 run 方法的 CommandLineRunner:
Java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
如果定义了多个 CommandLineRunner 或 ApplicationRunner bean,并且必须按特定顺序调用它们,还可以实现 org.springframework.core.Ordered 接口或使用 org.springframework.core.annotation.Order 注解。
1.11. 应用程序退出
每个 SpringApplication 都会向 JVM 注册一个关闭钩子,以确保 ApplicationContext 在退出时优雅地关闭。所有标准的 Spring 生命周期回调(例如 DisposableBean 接口或 @PreDestroy 注解)都可以使用。
此外,如果 bean 希望在调用 SpringApplication.exit() 时返回特定的退出代码,可以实现 org.springframework.boot.ExitCodeGenerator 接口。然后可以将此退出代码传递给 System.exit() 以将其作为状态代码返回,如下例所示:
Java
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)));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
此外,ExitCodeGenerator 接口还可以由异常实现。遇到此类异常时,Spring Boot 会返回由实现的 getExitCode() 方法提供的退出代码。
如果有多个 ExitCodeGenerator,则使用生成的第一个非零退出代码。要控制调用生成器的顺序,还可以实现 org.springframework.core.Ordered 接口或使用 org.springframework.core.annotation.Order 注解。
1.12. 管理员功能
可以通过指定 spring.application.admin.enabled 属性来为应用程序启用与管理员相关的功能。这会在平台 MBeanServer 上公开 SpringApplicationAdminMXBean。您可以使用此功能远程管理您的 Spring Boot 应用程序。此功能对于任何服务包装器实现也可能很有用。
Note:如果您想知道应用程序运行在哪个 HTTP 端口上,请获取键为
local.server.port的属性。
1.13. 应用程序启动跟踪
在应用程序启动期间,SpringApplication 和 ApplicationContext 执行许多与应用程序生命周期、bean 生命周期或甚至处理应用程序事件相关的任务。使用 ApplicationStartup,Spring Framework 允许您使用 StartupStep 对象跟踪应用程序启动序列。此数据可以收集用于分析目的,或者只是为了更好地了解应用程序启动过程。
您可以在设置 SpringApplication 实例时选择 ApplicationStartup 实现。例如,要使用 BufferingApplicationStartup,可以编写:
Java
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);
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
第一个可用实现 FlightRecorderApplicationStartup 由 Spring Framework 提供。它将 Spring 特定的启动事件添加到 Java Flight Recorder 会话中,并用于分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(如分配、GC、类加载…)相关联。配置后,可以通过启用 Flight Recorder 来记录数据:
Bash
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jarSpring Boot 附带 BufferingApplicationStartup 变体;此实现用于缓冲启动步骤并将它们排入外部度量系统。应用程序可以在任何组件中请求类型为 BufferingApplicationStartup 的 bean。
Spring Boot 还可以配置为公开一个 startup 端点,该端点以 JSON 文档的形式提供此信息。
2. 外部化配置
Spring Boot 允许您外部化配置,以便在不同环境中使用相同的应用程序代码。您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用 @Value 注解直接注入到您的 bean 中,通过 Spring 的 Environment 抽象访问,或者通过 @ConfigurationProperties 绑定到结构化对象。
Spring Boot 使用一个非常特定的 PropertySource 顺序,该顺序旨在允许合理地覆盖值。后续的属性源可以覆盖前面定义的值。源按以下顺序考虑:
默认属性(通过设置
SpringApplication.setDefaultProperties指定)。在
@Configuration类上的@PropertySource注解。请注意,这些属性源在应用程序上下文刷新之前不会添加到Environment中。这对于在刷新开始之前读取的某些属性(如logging.*和spring.main.*)来说太晚了。配置数据(例如
application.properties文件)。一个
RandomValuePropertySource,其中只有random.*属性。操作系统环境变量。
Java 系统属性(
System.getProperties())。JNDI 属性从
java:comp/env。ServletContext初始化参数。ServletConfig初始化参数。SPRING_APPLICATION_JSON中的属性(嵌入在环境变量或系统属性中的内联 JSON)。命令行参数。
测试中的
properties属性。可用于@SpringBootTest和测试特定应用程序切片的注解。测试中的
@DynamicPropertySource注解。测试中的
@TestPropertySource注解。当 devtools 处于活动状态时,
$HOME/.config/spring-boot目录中的 devtools 全局设置属性。
配置数据文件按以下顺序考虑:
应用程序属性打包在您的 jar 中(
application.properties和 YAML 变体)。特定配置文件的应用程序属性打包在您的 jar 中(
application-{profile}.properties和 YAML 变体)。应用程序属性在您的打包 jar 外部(
application.properties和 YAML 变体)。特定配置文件的应用程序属性在您的打包 jar 外部(
application-{profile}.properties和 YAML 变体)。
Tip:建议在整个应用程序中坚持使用一种格式。如果您在同一位置有
.properties和 YAML 格式的配置文件,则.properties优先。
Tip:如果您使用环境变量而不是系统属性,大多数操作系统不允许使用带有句点分隔的键名,但您可以使用下划线代替(例如,
SPRING_CONFIG_NAME代替spring.config.name)。有关详细信息,请参阅从环境变量绑定。
Tip:如果您的应用程序在 Servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在
java:comp/env中)或 Servlet 上下文初始化参数,而不是或与环境变量或系统属性一起使用。
为了提供一个具体的例子,假设您开发了一个使用 name 属性的 @Component,如下例所示:
Java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
在您的应用程序类路径(例如,在您的 jar 内)中,您可以有一个 application.properties 文件,该文件为 name 提供一个合理的默认属性值。在新环境中运行时,可以在 jar 外部提供一个 application.properties 文件来覆盖 name。对于一次性测试,您可以使用特定的命令行开关启动(例如,java -jar app.jar --name="Spring")。
Note:
env和configprops端点可能对确定属性具有特定值很有用。您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参阅 “生产就绪功能” 部分。
2.1. 访问命令行属性
默认情况下,SpringApplication 将任何命令行选项参数(即以 -- 开头的参数,例如 --server.port=9000)转换为 property,并将其添加到 Spring Environment 中。如前所述,命令行属性始终优先于基于文件的属性源。
如果您不希望将命令行属性添加到 Environment 中,可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它们。
2.2. JSON 应用程序属性
环境变量和系统属性通常有限制,这意味着某些属性名称无法使用。为了帮助解决这个问题,Spring Boot 允许您将一组属性编码为单个 JSON 结构。
当您的应用程序启动时,任何 spring.application.json 或 SPRING_APPLICATION_JSON 属性都会被解析并添加到 Environment 中。
例如,可以在 UN*X shell 中将 SPRING_APPLICATION_JSON 属性作为环境变量提供给命令行:
Bash
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar在上面的例子中,您最终会在 Spring Environment 中得到 my.name=test。
相同的 JSON 也可以作为系统属性提供:
Bash
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar或者,您可以使用命令行参数提供 JSON:
Bash
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'如果您部署到经典的应用程序服务器,您还可以使用名为 java:comp/env/spring.application.json 的 JNDI 变量。
Tip:尽管
null值从 JSON 中添加到生成的属性源中,但PropertySourcesPropertyResolver将null属性视为缺失值。这意味着 JSON 无法用null值覆盖较低顺序的属性源中的属性。
2.3. 外部应用程序属性
Spring Boot 会在应用程序启动时自动查找并加载 application.properties 和 application.yaml 文件,位置如下:
- 从类路径
- 类路径根
- 类路径
/config包
- 从当前目录
- 当前目录
- 当前目录中的
config/子目录 config/子目录的直接子目录
列表按优先级顺序排列(较低项的值覆盖较早项的值)。文档从加载的文件中添加为 PropertySources 到 Spring Environment 中。
如果您不喜欢 application 作为配置文件名,可以通过指定 spring.config.name 环境属性切换到另一个文件名。例如,要查找 myproject.properties 和 myproject.yaml 文件,可以运行您的应用程序如下:
Bash
$ java -jar myproject.jar --spring.config.name=myproject您还可以使用 spring.config.location 环境属性引用显式位置。此属性接受一个或多个位置的逗号分隔列表。
以下示例显示了如何指定两个不同的文件:
Bash
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.propertiesNote:如果位置是可选的并且您不介意它们不存在,请使用前缀
optional:。
Warning:
spring.config.name、spring.config.location和spring.config.additional-location在很早的时候使用,以确定需要加载哪些文件。它们必须定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。
如果 spring.config.location 包含目录(而不是文件),它们应以 / 结尾。在运行时,它们将在加载之前附加从 spring.config.name 生成的名称。spring.config.location 中指定的文件将直接导入。
Tip:目录和文件位置值也会扩展以检查特定配置文件。例如,如果您有一个
spring.config.location的classpath:myconfig.properties,您还会发现适当的classpath:myconfig-<profile>.properties文件被加载。
在大多数情况下,您添加的每个 spring.config.location 项都会引用单个文件或目录。位置按定义的顺序处理,后续位置可以覆盖较早位置的值。
如果您有复杂的位置设置,并且使用特定配置文件,您可能需要提供更多提示,以便 Spring Boot 知道如何对它们进行分组。位置组是一组在同一级别考虑的位置。例如,您可能希望将所有类路径位置分组,然后是所有外部位置。组内的项目应以 ; 分隔。有关详细信息,请参阅 “特定配置文件” 部分中的示例。
使用 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 选择的任何其他基名称)中为应用程序提供默认值。然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。
2.3.1. 可选位置
默认情况下,如果指定的配置数据位置不存在,Spring Boot 将抛出 ConfigDataLocationNotFoundException,并且您的应用程序将无法启动。
如果您希望指定一个位置,但不介意它不总是存在,可以使用 optional: 前缀。您可以将此前缀与 spring.config.location 和 spring.config.additional-location 属性一起使用,以及 spring.config.import 声明。
例如,spring.config.import 值 optional:file:./myconfig.properties 允许您的应用程序启动,即使 myconfig.properties 文件丢失。
如果您希望忽略所有 ConfigDataLocationNotFoundExceptions 并始终继续启动您的应用程序,可以使用 spring.config.on-not-found 属性。将值设置为 ignore,使用 SpringApplication.setDefaultProperties(…) 或系统/环境变量。
2.3.2. 通配符位置
如果配置文件位置包含最后一个路径段中的 * 字符,则它被视为通配符位置。通配符在加载配置时扩展,以便检查直接子目录。通配符位置在环境中非常有用,例如 Kubernetes,其中有多个配置属性源。
例如,如果您有一些 Redis 配置和一些 MySQL 配置,您可能希望将这两个配置分开,同时要求这两个配置都存在于 application.properties 文件中。这可能会导致两个单独的 application.properties 文件挂载在不同位置,例如 /config/redis/application.properties 和 /config/mysql/application.properties。在这种情况下,通配符位置 config/*/ 将导致处理这两个文件。
默认情况下,Spring Boot 包括 config/*/ 在默认搜索位置中。这意味着 /config 目录外部 jar 的所有子目录都将被搜索。
您还可以使用 spring.config.location 和 spring.config.additional-location 属性自行使用通配符位置。
Tip:通配符位置必须仅包含一个
*,并以*/结尾以用于目录的搜索位置,或以*/<filename>结尾以用于文件的搜索位置。包含通配符的位置按绝对路径的文件名的字母顺序排序。
Note:通配符位置仅适用于外部目录。您不能在
classpath:位置中使用通配符。
2.3.3. 特定配置文件
除了 application 属性文件之外,Spring Boot 还会尝试使用 application-{profile} 命名约定加载特定配置文件。例如,如果您的应用程序激活了名为 prod 的配置文件并使用 YAML 文件,则 application.yaml 和 application-prod.yaml 都会被考虑。
特定配置文件的属性从与标准 application.properties 相同的位置加载,特定配置文件始终覆盖非特定配置文件。如果指定了多个配置文件,则应用最后胜出策略。例如,如果 spring.profiles.active 属性指定了 prod,live,则 application-prod.properties 中的值可以被 application-live.properties 中的值覆盖。
Tip
最后胜出策略适用于位置组级别。
spring.config.location的classpath:/cfg/,classpath:/ext/不会与classpath:/cfg/;classpath:/ext/具有相同的覆盖规则。例如,继续我们上面的
prod,live示例,我们可能有以下文件:Text/cfg application-live.properties /ext application-live.properties application-prod.properties当我们有
spring.config.location的classpath:/cfg/,classpath:/ext/时,我们在所有/cfg文件之前处理所有/ext文件:
/cfg/application-live.properties/ext/application-prod.properties/ext/application-live.properties当我们有
classpath:/cfg/;classpath:/ext/时(使用;分隔符),我们在同一级别处理/cfg和/ext:
/ext/application-prod.properties/cfg/application-live.properties/ext/application-live.properties
Environment 有一组默认配置文件(默认为 [default]),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果未明确激活任何配置文件,则会考虑 application-default 中的属性。
Tip:属性文件只会加载一次。如果您已经直接导入了特定配置文件属性文件,则不会再次导入它。
2.3.4. 导入额外数据
应用程序属性可以使用 spring.config.import 属性从其他位置导入其他配置数据。导入在发现时处理,并被视为添加到声明导入的文档中的额外文档。
例如,您可能在类路径 application.properties 文件中有以下内容:
Properties
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties这将触发导入当前目录中的 dev.properties 文件(如果存在此类文件)。导入的 dev.properties 中的值将优先于触发导入的文件中的值。在上面的示例中,dev.properties 可以将 spring.application.name 重新定义为不同的值。
导入只会导入一次,无论声明多少次。声明导入的顺序在单个文档中不重要。例如,以下两个示例产生相同的结果:
Properties
spring.config.import=my.properties
my.property=valueProperties
my.property=value
spring.config.import=my.properties在上面的两个示例中,my.properties 文件中的值将优先于触发其导入的文件中的值。
可以在单个 spring.config.import 键下指定多个位置。位置将按定义的顺序处理,后续导入优先。
Tip:在适当的情况下,还会考虑特定配置文件的变体。上面的示例还会导入
my.properties以及任何my-<profile>.properties变体。
Note
Spring Boot 包含可插拔的 API,允许支持各种不同的位置地址。默认情况下,您可以导入 Java 属性、YAML 和 “配置树”。
第三方 jar 可以为其他技术提供支持(文件无需是本地的)。例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。
如果您要支持自己的位置,请参阅
org.springframework.boot.context.config包中的ConfigDataLocationResolver和ConfigDataLoader类。
2.3.5. 导入无扩展名的文件
某些云平台无法为挂载的卷文件添加文件扩展名。要导入这些无扩展名的文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过在方括号中放置扩展名提示来做到这一点。
例如,假设您有一个 /etc/config/myconfig 文件,您希望将其作为 yaml 导入。您可以从 application.properties 使用以下内容导入它:
Properties
spring.config.import=file:/etc/config/myconfig[.yaml]2.3.6. 使用配置树
在云平台(例如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。使用环境变量进行此类操作并不罕见,但这可能会有缺点,特别是如果该值应保密。
作为环境变量的替代方案,许多云平台现在允许您将配置映射到挂载的数据卷中。例如,Kubernetes 可以将 ConfigMaps 和 Secrets 都挂载到卷中。
有两种常见的卷挂载模式可以使用:
单个文件包含一组完整的属性(通常以 YAML 编写)。
多个文件写入目录树,文件名成为 “键”,内容成为 “值”。
对于第一种情况,您可以使用 spring.config.import 直接导入 YAML 或 Properties 文件,如上面所述。对于第二种情况,您需要使用 configtree: 前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。
例如,假设 Kubernetes 挂载了以下卷:
Text
etc/
config/
myapp/
username
passwordusername 文件的内容将是配置值,password 文件的内容将是秘密。
要导入这些属性,您可以将以下内容添加到 application.properties 或 application.yaml 文件中:
Properties
spring.config.import=optional:configtree:/etc/config/然后,您可以像往常一样从 Environment 访问或注入 myapp.username 和 myapp.password 属性。
Note:配置树中的文件夹和文件名形成属性名。在上面的示例中,要将属性访问为
username和password,您可以将spring.config.import设置为optional:configtree:/etc/config/myapp。
Tip:带有点符号的文件名也会正确映射。例如,在上面的示例中,
/etc/config中名为myapp.username的文件将导致Environment中的myapp.username属性。
Note:配置树值可以根据预期的内容绑定到
String和byte[]类型。
如果您有多个配置树要从同一父文件夹导入,可以使用通配符快捷方式。任何以 /*/ 结尾的 configtree: 位置都会将所有直接子文件夹导入为配置树。与非通配符导入一样,每个配置树中的文件夹和文件名形成属性名。
例如,给定以下卷:
Text
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password您可以使用 configtree:/etc/config/*/ 作为导入位置:
Properties
spring.config.import=optional:configtree:/etc/config/*/这将添加 db.username、db.password、mq.username 和 mq.password 属性。
Tip:使用通配符导入的目录按字母顺序排序。如果您需要不同的顺序,则应将每个位置列为单独的导入。
配置树还可以用于 Docker 机密。当授予 Docker 群集服务访问机密时,该机密会挂载到容器中。例如,如果名为 db.password 的机密挂载在位置 /run/secrets/,您可以使用以下内容使 db.password 可用于 Spring 环境:
Properties
spring.config.import=optional:configtree:/run/secrets/2.3.7. 属性占位符
application.properties 和 application.yaml 中的值通过现有的 Environment 进行过滤,因此您可以引用先前定义的值(例如,从系统属性或环境变量)。标准 ${name} 属性占位符语法可以在值的任何位置使用。属性占位符还可以使用 : 分隔默认值和属性名称来指定默认值,例如 ${name:default}。
以下示例显示了使用和不使用默认值的占位符:
Properties
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}假设 username 属性未在其他地方设置,app.description 将具有值 MyApp is a Spring Boot application written by Unknown。
Tip
您应始终使用其规范形式(使用小写字母的 kebab-case)引用占位符中的属性名称。这将允许 Spring Boot 使用与在松散绑定
@ConfigurationProperties时使用相同的逻辑。例如,
${demo.item-price}将选择demo.item-price和demo.itemPrice形式的application.properties文件,以及系统环境中的DEMO_ITEMPRICE。如果您使用${demo.itemPrice},则不会考虑demo.item-price和DEMO_ITEMPRICE。
Note:您还可以使用此技术创建现有 Spring Boot 属性的 “短” 变体。有关详细信息,请参阅使用 “短” 命令行参数操作指南。
2.3.8. 使用多文档文件
Spring Boot 允许您将单个物理文件拆分为多个逻辑文档,每个文档都会独立添加。文档从上到下按顺序处理。后续文档可以覆盖前面文档中定义的属性。
对于 application.yaml 文件,使用标准 YAML 多文档语法。三个连续的破折号表示一个文档的结束和下一个文档的开始。
例如,以下文件有两个逻辑文档:
YAML
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"对于 application.properties 文件,使用特殊的 #--- 或 !--- 注释来标记文档拆分:
Properties
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetesTip:属性文件分隔符不得有任何前导空白,并且必须恰好有三个破折号字符。分隔符前后的行不得有相同的注释前缀。
Note:多文档属性文件通常与激活属性(例如
spring.config.activate.on-profile)一起使用。有关详细信息,请参阅下一节。
Warning:不能使用
@PropertySource或@TestPropertySource注解加载多文档属性文件。
2.3.9. 激活属性
有时,仅在满足某些条件时激活一组属性很有用。例如,您可能有一些属性,这些属性仅在特定配置文件处于活动状态时才相关。
您可以使用 spring.config.activate.* 有条件地激活属性文档。
以下激活属性可用:
| 属性 | 注释 |
|---|---|
on-profile | 必须匹配的配置文件表达式,以使文档处于活动状态。 |
on-cloud-platform | 必须检测到的 CloudPlatform,以使文档处于活动状态。 |
例如,以下内容指定第二个文档仅在 Kubernetes 上运行时处于活动状态,并且仅在 “prod” 或 “staging” 配置文件处于活动状态时处于活动状态:
Properties
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set2.4. 加密属性
Spring Boot 不提供任何内置支持来加密属性值,但它确实提供了修改 Spring Environment 中包含的值所需的钩点。EnvironmentPostProcessor 接口允许您在应用程序启动之前操作 Environment。有关详细信息,请参阅在启动之前自定义 Environment 或 ApplicationContext。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目提供了对在 HashiCorp Vault 中存储外部化配置的支持。
2.5. 使用 YAML
YAML 是 JSON 的超集,因此是一种方便的格式,用于指定层次配置数据。SpringApplication 类在类路径上有 SnakeYAML 库时,自动支持 YAML 作为属性的替代方案。
Tip:如果您使用 “启动器”,SnakeYAML 会由
spring-boot-starter自动提供。
2.5.1. 将 YAML 映射到属性
YAML 文档需要从其层次结构转换为可以与 Spring Environment 一起使用的平面结构。例如,考虑以下 YAML 文档:
YAML
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"为了从 Environment 访问这些属性,它们将被展平如下:
Properties
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:
YAML
my:
servers:
- "dev.example.com"
- "another.example.com"上面的示例将转换为以下属性:
Properties
my.servers[0]=dev.example.com
my.servers[1]=another.example.comNote:使用
[index]表示法的属性可以绑定到 JavaList或Set对象,使用 Spring Boot 的Binder类。有关详细信息,请参阅 “类型安全配置属性” 部分。
Warning:YAML 文件不能使用
@PropertySource或@TestPropertySource注解加载。因此,如果您需要以这种方式加载值,则需要使用属性文件。
2.5.2. 直接加载 YAML
Spring Framework 提供了两个方便的类,可以用来加载 YAML 文档。YamlPropertiesFactoryBean 将 YAML 加载为 Properties,YamlMapFactoryBean 将 YAML 加载为 Map。
您还可以使用 YamlPropertySourceLoader 类,如果您希望将 YAML 加载为 Spring PropertySource。
2.6. 配置随机值
RandomValuePropertySource 对于注入随机值(例如,到秘密或测试用例中)非常有用。它可以生成整数、长整数、UUID 或字符串,如下例所示:
Properties
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 是最大值(不包括)。
2.7. 配置系统环境属性
Spring Boot 支持为系统环境属性设置前缀。如果系统环境由多个 Spring Boot 应用程序共享,并且具有不同的配置要求,则这非常有用。系统环境属性的前缀可以直接在 SpringApplication 上设置。
例如,如果您将前缀设置为 input,则属性(例如 remote.timeout)也将解析为系统环境中的 input.remote.timeout。
2.8. 类型安全配置属性
使用 @Value("${property}") 注解注入配置属性有时会变得繁琐,特别是如果您处理多个属性或您的数据是层次结构时。Spring Boot 提供了一种替代方法来处理属性,该方法允许强类型 bean 管理和验证应用程序的配置。
Note:还可以参阅
@Value和类型安全配置属性之间的区别。
2.8.1. JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 bean,如下例所示:
Java
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;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
上面的 POJO 定义了以下属性:
my.service.enabled,默认值为false。my.service.remote-address,类型可以从String强制转换。my.service.security.username,具有嵌套的 “security” 对象,其名称由属性的名称确定。特别是,类型在那里根本没有使用,并且可以是SecurityProperties。my.service.security.password。my.service.security.roles,具有默认为USER的String集合。
Tip:映射到 Spring Boot 中可用的
@ConfigurationProperties类的属性是公共 API,但类本身的访问器(getter/setter)不是直接使用的。
Tip
这种安排依赖于默认的空构造函数和 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。可以省略 setter 的情况如下:
只要初始化了 Maps,就需要 getter,但不一定需要 setter,因为它们可以被绑定器改变。
集合和数组可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)访问。在后一种情况下,setter 是强制性的。我们建议始终为这些类型添加 setter。如果您初始化了一个集合,请确保它不是不可变的(如前面的示例)。
如果初始化了嵌套的 POJO 属性(如前面示例中的
Security字段),则不需要 setter。如果您希望绑定器使用其默认构造函数在运行时创建实例,则需要 setter。有些人使用 Project Lombok 自动添加 getter 和 setter。确保 Lombok 不为这种类型生成任何特定的构造函数,因为它会自动由容器用于实例化对象。
最后,只考虑标准的 Java Bean 属性,不支持对静态属性的绑定。
2.8.2. 构造函数绑定
前面部分的示例可以以不可变的方式重写,如下例所示:
Java
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;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
在这种设置中,使用 @ConstructorBinding 注解表示应使用构造函数绑定。这意味着绑定器将期望找到具有您希望绑定的参数的构造函数。如果您使用的是 Java 16 或更高版本,构造函数绑定可以与记录一起使用。在这种情况下,除非您的记录有多个构造函数,否则不需要使用 @ConstructorBinding。
嵌套成员 @ConstructorBinding 类(例如上面示例中的 Security)也将通过其构造函数绑定。
可以使用 @DefaultValue 在构造函数参数上或在使用 Java 16 或更高版本时在记录组件上指定默认值。转换服务将应用于将 String 值强制转换为缺少属性的目标类型。
参考前面的示例,如果没有属性绑定到 Security,则 MyProperties 实例将包含 security 的 null 值。要使其在没有属性绑定到它时(使用 Kotlin 时,这需要将 Security 的 username 和 password 参数声明为可空,因为它们没有默认值)包含 Security 的非空实例,请使用空的 @DefaultValue 注解:
Java
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}1
2
3
4
5
2
3
4
5
Tip:要使用构造函数绑定,必须使用
@EnableConfigurationProperties或配置属性扫描启用该类。您不能将构造函数绑定与由常规 Spring 机制创建的 bean 一起使用(例如@Componentbean、使用@Bean方法创建的 bean 或使用@Import加载的 bean)。
Note:如果您的类有多个构造函数,您还可以直接在应绑定的构造函数上使用
@ConstructorBinding。
Tip:不建议使用
java.util.Optional与@ConfigurationProperties,因为它主要用作返回类型。因此,它不适合配置属性注入。为了与其他类型的属性一致,如果您声明了一个Optional属性并且它没有值,则会绑定null而不是空的Optional。
2.8.3. 启用 @ConfigurationProperties 注解的类型
Spring Boot 提供了基础设施来绑定 @ConfigurationProperties 类型并将它们注册为 bean。您可以逐个启用配置属性类,也可以启用配置属性扫描,其工作方式类似于组件扫描。
有时,用 @ConfigurationProperties 注解的类可能不适合扫描,例如,如果您正在开发自己的自动配置或希望有条件地启用它们。在这些情况下,使用 @EnableConfigurationProperties 注解指定要处理的类型列表。这可以在任何 @Configuration 类中完成,如下例所示:
Java
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Java
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("some.properties")
public class SomeProperties {
}1
2
3
4
5
6
2
3
4
5
6
要使用配置属性扫描,请将 @ConfigurationPropertiesScan 注解添加到您的应用程序中。通常,它被添加到用 @SpringBootApplication 注解的主应用程序类中,但它可以添加到任何 @Configuration 类中。默认情况下,扫描将从声明注解的类的包中进行。如果您想定义要扫描的特定包,可以按如下示例所示进行:
Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Tip
当使用配置属性扫描或通过
@EnableConfigurationProperties注册@ConfigurationPropertiesbean 时,bean 具有一个约定名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties注解中指定的环境键前缀,<fqn>是 bean 的完全限定名称。如果注解没有提供任何前缀,则仅使用 bean 的完全限定名称。假设它在
com.example.app包中,上面SomeProperties示例的 bean 名称是some.properties-com.example.app.SomeProperties。
我们建议 @ConfigurationProperties 仅处理环境,并且特别不从上下文中注入其他 bean。对于特殊情况,可以使用 setter 注入或框架提供的任何 *Aware 接口(例如,如果您需要访问 Environment,则使用 EnvironmentAware)。如果您仍然希望使用构造函数注入其他 bean,则配置属性 bean 必须用 @Component 注解,并使用基于 JavaBean 的属性绑定。
2.8.4. 使用 @ConfigurationProperties 注解的类型
这种配置方式与 SpringApplication 外部 YAML 配置特别匹配,如下例所示:
YAML
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"要使用 @ConfigurationProperties bean,可以像注入任何其他 bean 一样注入它们,如下例所示:
Java
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();
// ...
}
// ...
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Note:使用
@ConfigurationProperties还可以生成元数据文件,IDE 可以使用这些文件为您自己的键提供自动补全。有关详细信息,请参阅附录。
2.8.5. 第三方配置
除了使用 @ConfigurationProperties 注解类之外,还可以使用它注解公共 @Bean 方法。这样做特别有用,当您希望将属性绑定到第三方组件时,这些组件超出了您的控制范围。
要从 Environment 属性配置 bean,请将 @ConfigurationProperties 添加到其 bean 注册中,如下例所示:
Java
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();
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
任何用 another 前缀定义的 JavaBean 属性都将映射到该 AnotherComponent bean,方式类似于前面的 SomeProperties 示例。
2.8.6. 松散绑定
Spring Boot 使用一些松散的规则将 Environment 属性绑定到 @ConfigurationProperties bean,因此不需要环境属性名称和 bean 属性名称之间的完全匹配。常见的情况是这很有用,例如短划线分隔的环境属性(例如,context-path 绑定到 contextPath)和大写的环境属性(例如,PORT 绑定到 port)。
例如,考虑以下 @ConfigurationProperties 类:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
使用上面的代码,可以使用以下属性名称:
| 属性 | 注释 |
|---|---|
my.main-project.person.first-name | Kebab case,建议在 .properties 和 YAML 文件中使用。 |
my.main-project.person.firstName | 标准的 camel case 语法。 |
my.main-project.person.first_name | 下划线表示法,是 .properties 和 YAML 文件中的替代格式。 |
MY_MAINPROJECT_PERSON_FIRSTNAME | 大写格式,建议在使用系统环境变量时使用。 |
Tip:注解的
prefix值必须是 kebab case(小写并用-分隔,例如my.main-project.person)。
| 属性源 | 简单 | 列表 |
|---|---|---|
| 属性文件 | Camel case、kebab case 或下划线表示法 | 标准列表语法使用 [ ] 或逗号分隔值 |
| YAML 文件 | Camel case、kebab case 或下划线表示法 | 标准 YAML 列表语法或逗号分隔值 |
| 环境变量 | 大写格式,下划线作为分隔符(请参阅从环境变量绑定)。 | 数值周围有下划线(请参阅从环境变量绑定) |
| 系统属性 | Camel case、kebab case 或下划线表示法 | 标准列表语法使用 [ ] 或逗号分隔值 |
Note:建议尽可能将属性存储在小写的 kebab 格式中,例如
my.person.first-name=Rod。
2.8.6.1. 绑定映射
在绑定到 Map 属性时,您可能需要使用特殊的方括号表示法,以便保留原始 key 值。如果键没有被 [] 包围,则会删除任何不是字母数字、- 或 . 的字符。
例如,考虑将以下属性绑定到 Map<String,String>:
Properties
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3Tip:对于 YAML 文件,方括号需要用引号括起来,以便正确解析键。
上面的属性将绑定到一个 Map,其中 /key1、/key2 和 key3 是映射中的键。斜杠已从 key3 中删除,因为它没有被方括号包围。
在绑定到标量值时,包含 . 的键不需要被 [] 包围。标量值包括枚举和 java.lang 包中的所有类型,Object 除外。绑定 a.b=c 到 Map<String, String> 将保留 . 并返回一个包含条目 {"a.b"="c"} 的映射。对于其他类型,您需要使用方括号表示法,如果您的 key 包含 .。例如,绑定 a.b=c 到 Map<String, Object> 将返回一个包含条目 {"a"={"b"="c"}} 的映射,而 [a.b]=c 将返回一个包含条目 {"a.b"="c"} 的映射。
2.8.6.2. 从环境变量绑定
大多数操作系统对环境变量名称施加严格的规则。例如,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 的环境变量。
2.8.6.3. 缓存
松散绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变的属性源。要自定义此行为,例如启用对可变属性源的缓存,请使用 ConfigurationPropertyCaching。
2.8.7. 合并复杂类型
当列表在多个位置配置时,覆盖通过替换整个列表进行。
例如,假设一个 MyPojo 对象具有 name 和 description 属性,它们默认为 null。以下示例公开了一个 MyProperties 中的 MyPojo 对象列表:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
考虑以下配置:
Properties
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 配置文件,则 list 仍然 仅包含一个条目(名称为 my another name,描述为 null)。此配置 不会 向列表添加第二个 MyPojo 实例,也不会合并条目。
当 List 在多个配置文件中指定时,优先级最高的配置文件(仅该配置文件)将被使用。考虑以下示例:
Properties
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 条目(名称为 my another name,描述为 null)。对于 YAML,逗号分隔的列表和 YAML 列表都可以用于完全覆盖列表的内容。
对于 Map 属性,您可以绑定具有从多个来源提取的属性值。但是,对于同一属性在多个来源中,优先级最高的属性将被使用。以下示例公开了一个 Map<String, MyPojo> 从 MyProperties:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
考虑以下配置:
Properties
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 包含一个键为 key1 的条目(名称为 my name 1,描述为 my description 1)。如果启用了 dev 配置文件,则 map 包含两个条目,键为 key1(名称为 dev name 1,描述为 my description 1)和 key2(名称为 dev name 2,描述为 dev description 2)。
Tip:上述合并规则适用于所有属性源的属性,而不仅仅是文件。
2.8.8. 属性转换
Spring Boot 尝试将外部应用程序属性强制转换为正确的类型,当它绑定到 @ConfigurationProperties bean 时。如果您需要自定义类型转换,可以提供一个 ConversionService bean(名为 conversionService)或自定义属性编辑器(通过 CustomEditorConfigurer bean)或自定义 Converters(使用 @ConfigurationPropertiesBinding 注解的 bean 定义)。
Tip:由于此 bean 在应用程序生命周期的非常早期请求,请确保限制
ConversionService使用的依赖项。通常,您可能需要的任何依赖项在创建时可能尚未完全初始化。如果不需要配置键强制转换,您可能希望重命名自定义ConversionService,并仅依赖于限定为@ConfigurationPropertiesBinding的自定义转换器。
2.8.8.1. 转换持续时间
Spring Boot 对表示持续时间具有专用支持。如果您公开一个 java.time.Duration 属性,则在应用程序属性中可以使用以下格式:
一个常规的
long表示(使用毫秒作为默认单位,除非指定了@DurationUnit)标准 ISO-8601 格式由
java.time.Duration使用一个更可读的格式,其中值和单位成对出现(
10s表示 10 秒)
考虑以下示例:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
要指定 30 秒的会话超时,30、PT30S 和 30s 都是等效的。读取超时 500 毫秒可以以以下任何形式指定:500、PT0.5S 和 500ms。
您还可以使用任何受支持的单位。这些是:
ns表示纳秒us表示微秒ms表示毫秒s表示秒m表示分钟h表示小时d表示天
默认单位是毫秒,可以使用 @DurationUnit 覆盖,如上面的示例所示。
如果您更喜欢使用构造函数绑定,可以公开相同的属性,如下例所示:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Note:如果您正在升级
Long属性,请确保定义单位(使用@DurationUnit),如果它不是毫秒。这样做可以提供透明的升级路径,同时支持更丰富的格式。
2.8.8.2. 转换周期
除了持续时间之外,Spring Boot 还可以处理 java.time.Period 类型。以下格式可以在应用程序属性中使用:
一个常规的
int表示(使用天作为默认单位,除非指定了@PeriodUnit)标准 ISO-8601 格式由
java.time.Period使用一个更简单的格式,其中值和单位对成对出现(
1y3d表示 1 年和 3 天)
支持以下单位的简单格式:
y表示年m表示月w表示周d表示天
Tip:
java.time.Period类型实际上从不存储周的数量,它是一个捷径,表示 “7 天”。
2.8.8.3. 转换数据大小
Spring Framework 具有一个 DataSize 值类型,表示以字节为单位的大小。如果您公开一个 DataSize 属性,则在应用程序属性中可以使用以下格式:
一个常规的
long表示(使用字节作为默认单位,除非指定了@DataSizeUnit)一个更可读的格式,其中值和单位成对出现(
10MB表示 10 兆字节)
考虑以下示例:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
要指定 10 兆字节的缓冲区大小,10 和 10MB 是等效的。大小阈值 256 字节可以指定为 256 或 256B。
您还可以使用任何受支持的单位。这些是:
B表示字节KB表示千字节MB表示兆字节GB表示千兆字节TB表示太字节
默认单位是字节,可以使用 @DataSizeUnit 覆盖,如上面的示例所示。
如果您更喜欢使用构造函数绑定,可以公开相同的属性,如下例所示:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Note:如果您正在升级
Long属性,请确保定义单位(使用@DataSizeUnit),如果它不是字节。这样做可以提供透明的升级路径,同时支持更丰富的格式。
2.8.9. @ConfigurationProperties 验证
Spring Boot 尝试在 @ConfigurationProperties 类注解有 Spring 的 @Validated 注解时验证这些类。您可以直接在配置类上使用 JSR-303 javax.validation 约束注解。要做到这一点,请确保类路径上有符合 JSR-303 的实现,然后将约束注解添加到您的字段中,如下例所示:
Java
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;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Note:您还可以通过注解创建配置属性的
@Bean方法来触发验证。
为了确保始终为嵌套属性触发验证,即使没有找到属性,也必须使用 @Valid 注解相关字段。以下示例基于前面的 MyProperties 示例:
Java
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;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
您还可以通过创建名为 configurationPropertiesValidator 的 bean 定义来添加自定义 Spring Validator。@Bean 方法应声明为 static。配置属性验证器在应用程序生命周期的非常早期创建,并将 @Bean 方法声明为静态可以让 bean 在不实例化 @Configuration 类的情况下创建。这样做可以避免由早期实例化引起的任何问题。
Note:
spring-boot-actuator模块包括一个公开所有@ConfigurationPropertiesbean 的端点。将您的 Web 浏览器指向/actuator/configprops或使用等效的 JMX 端点。有关详细信息,请参阅 “生产就绪功能” 部分。
2.8.10. @ConfigurationProperties 与 @Value
@Value 注解是核心容器功能,它不提供与类型安全配置属性相同的功能。以下表格总结了 @ConfigurationProperties 和 @Value 支持的功能:
| 功能 | @ConfigurationProperties | @Value |
|---|---|---|
| 松散绑定 | 是 | 有限(请参阅下面的注释) |
| 元数据支持 | 是 | 否 |
SpEL 评估 | 否 | 是 |
Tip
如果您确实想使用
@Value,我们建议您使用其规范形式(kebab-case 仅使用小写字母)引用属性名称。这将允许 Spring Boot 使用与@ConfigurationProperties松散绑定相同的逻辑。例如,
@Value("${demo.item-price}")将捕获application.properties文件中的demo.item-price和demo.itemPrice形式,以及系统环境中的DEMO_ITEMPRICE。如果您使用@Value("${demo.itemPrice}"),则不会考虑demo.item-price和DEMO_ITEMPRICE。
如果您为自己的组件定义了一组配置键,我们建议您将它们分组到一个 POJO 中,并用 @ConfigurationProperties 注解。这样做将为您提供一个结构化的、类型安全的对象,您可以将其注入到自己的 bean 中。
SpEL 表达式从应用程序属性文件在解析这些文件并填充环境时不会被处理。但是,可以在 @Value 中编写 SpEL 表达式。如果应用程序属性文件中的属性值是 SpEL 表达式,则在通过 @Value 消费时将对其进行评估。
3. 配置文件
Spring 配置文件提供了一种方法来分隔应用程序配置的部分,并使其仅在特定环境中可用。任何 @Component、@Configuration 或 @ConfigurationProperties 都可以使用 @Profile 标记来限制其加载,如下例所示:
Java
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Tip:如果
@ConfigurationProperties豆通过@EnableConfigurationProperties注册而不是自动扫描,则需要在具有@EnableConfigurationProperties注解的@Configuration类上指定@Profile注解。在@ConfigurationProperties被扫描的情况下,@Profile可以指定在@ConfigurationProperties类本身上。
您可以使用 spring.profiles.active Environment 属性来指定哪些配置文件是活动的。您可以按照本章前面描述的任何一种方式指定该属性。例如,您可以在 application.properties 中包含它,如下例所示:
Properties
spring.profiles.active=dev,hsqldb您还可以通过以下命令行开关指定它:--spring.profiles.active=dev,hsqldb。
如果没有活动的配置文件,则启用默认配置文件。默认配置文件的名称为 default,可以使用 spring.profiles.default Environment 属性进行调整,如下例所示:
Properties
spring.profiles.default=nonespring.profiles.active 和 spring.profiles.default 只能在非特定配置文件中使用。这意味着它们不能包含在特定配置文件或通过 spring.config.activate.on-profile 激活的文档中。
例如,第二个文档配置是无效的:
Properties
# 此文档有效
spring.profiles.active=prod
#---
# 此文档无效
spring.config.activate.on-profile=prod
spring.profiles.active=metrics3.1. 添加活动配置文件
spring.profiles.active 属性遵循与其他属性相同的排序规则:最高的 PropertySource 优先。这意味着您可以在 application.properties 中指定活动配置文件,然后使用命令行开关替换它们。
有时,拥有添加到活动配置文件而不是替换它们的属性是有用的。spring.profiles.include 属性可以用于在 spring.profiles.active 属性激活的配置文件之上添加活动配置文件。SpringApplication 入口点还具有用于设置附加配置文件的 Java API。请参阅 SpringApplication 中的 setAdditionalProfiles() 方法。
例如,当运行具有以下属性的应用程序时,common 和 local 配置文件将被激活,即使它使用 --spring.profiles.active 开关运行:
Properties
spring.profiles.include[0]=common
spring.profiles.include[1]=localWarning:与
spring.profiles.active类似,spring.profiles.include只能在非特定配置文件中使用。这意味着它不能包含在特定配置文件或通过spring.config.activate.on-profile激活的文档中。
配置文件组,如下一节所述,也可以用于在给定配置文件活动时添加活动配置文件。
3.2. 配置文件组
有时您定义和使用的配置文件过于细粒度,使用起来很麻烦。例如,您可能有 proddb 和 prodmq 配置文件,用于分别启用数据库和消息传递功能。
为了帮助解决这个问题,Spring Boot 允许您定义配置文件组。配置文件组允许您为相关配置文件组定义一个逻辑名称。
例如,我们可以创建一个由我们的 proddb 和 prodmq 配置文件组成的 production 组。
Properties
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq我们的应用程序现在可以使用 --spring.profiles.active=production 启动,以一次性激活 production、proddb 和 prodmq 配置文件。
3.3. 以编程方式设置配置文件
您可以通过在应用程序运行之前调用 SpringApplication.setAdditionalProfiles(…) 以编程方式设置活动配置文件。还可以使用 Spring 的 ConfigurableEnvironment 接口激活配置文件。
3.4. 特定配置文件的配置文件
application.properties(或 application.yaml)和通过 @ConfigurationProperties 引用的文件的特定配置文件变体被视为文件并加载。有关详细信息,请参阅 “特定配置文件”。
4. 日志记录
Spring Boot 使用 Commons Logging 进行所有内部日志记录,但保留底层日志实现开放。为 Java Util Logging、Log4J2 和 Logback 提供了默认配置。在每种情况下,日志记录器都预先配置为使用控制台输出,还可以选择文件输出。
默认情况下,如果您使用 “启动器”,则使用 Logback 进行日志记录。还包括适当的 Logback 路由,以确保依赖于 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的库都能正常工作。
Note:有很多日志记录框架可用于 Java。如果上述列表看起来令人困惑,请不要担心。通常,您不需要更改日志记录依赖项,Spring Boot 的默认设置就可以正常工作。
Note:当您将应用程序部署到 servlet 容器或应用程序服务器时,使用 Java Util Logging API 进行的日志记录不会路由到您的应用程序日志中。这可以防止容器或其他已部署到其中的应用程序执行的日志记录出现在您的应用程序日志中。
4.1. 日志格式
Spring Boot 的默认日志输出如下例所示:
Text
2023-11-23 07:23:18.861 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28351 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23 07:23:18.868 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default"
2023-11-23 07:23:20.194 INFO 28351 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-11-23 07:23:20.209 INFO 28351 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-11-23 07:23:20.209 INFO 28351 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.83]
2023-11-23 07:23:20.342 INFO 28351 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-11-23 07:23:20.342 INFO 28351 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1404 ms
2023-11-23 07:23:20.946 INFO 28351 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-23 07:23:20.962 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.7 seconds (JVM running for 3.157)输出的项目包括:
- 日期和时间:毫秒精度且易于排序。
- 日志级别:
ERROR、WARN、INFO、DEBUG或TRACE。 - 进程 ID。
- 一个
---分隔符,用于区分实际日志消息的开始。 - 线程名称:用方括号括起来(可能会截断以进行控制台输出)。
- 日志记录器名称:这通常是源类名称(通常缩写)。
- 日志消息。
Tip:Logback 没有
FATAL级别。它被映射到ERROR。
4.2. 控制台输出
默认日志配置将消息回显到控制台,因为它们被写入。默认情况下,记录 ERROR 级别、WARN 级别和 INFO 级别的消息。您还可以通过以 --debug 标志启动应用程序来启用 “调试” 模式。
Bash
$ java -jar myapp.jar --debugTip:您还可以在
application.properties中指定debug=true。
启用调试模式时,选择核心日志记录器(嵌入式容器、Hibernate 和 Spring Boot)被配置为输出更多信息。启用调试模式不会配置您的应用程序记录所有 DEBUG 级别的消息。
或者,您可以通过以 --trace 标志(或在 application.properties 中指定 trace=true)启动应用程序来启用 “跟踪” 模式。这样做将启用对核心日志记录器(嵌入式容器、Hibernate 模式生成和整个 Spring 组合)的跟踪日志记录。
4.2.1. 彩色编码输出
如果您的终端支持 ANSI,则使用彩色输出以帮助可读性。您可以将 spring.output.ansi.enabled 设置为支持的值以覆盖自动检测。
彩色编码由使用 %clr 转换字配置。在其最简单的形式中,转换器根据日志级别对输出进行着色,如下例所示:
Text
%clr(%5p)下表描述了日志级别到颜色的映射:
| 级别 | 颜色 |
|---|---|
FATAL | 红色 |
ERROR | 红色 |
WARN | 黄色 |
INFO | 绿色 |
DEBUG | 绿色 |
TRACE | 绿色 |
或者,您可以通过将其作为选项提供给转换来指定要使用的颜色或样式。例如,要使文本变为黄色,请使用以下设置:
Text
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}支持以下颜色和样式:
bluecyanfaintgreenmagentaredyellow
4.3. 文件输出
默认情况下,Spring Boot 仅记录到控制台,不写入日志文件。如果您希望除控制台输出之外还写入日志文件,则需要设置 logging.file.name 或 logging.file.path 属性(例如,在您的 application.properties 中)。
下表显示了如何一起使用 logging.* 属性:
logging.file.name | logging.file.path | 示例 | 描述 |
|---|---|---|---|
| (none) | (none) | 仅控制台日志记录。 | |
| 特定文件 | (none) | my.log | 写入指定的日志文件。名称可以是确切位置或相对于当前目录。 |
| (none) | 特定目录 | /var/log | 将 spring.log 写入指定目录。名称可以是确切位置或相对于当前目录。 |
日志文件在达到 10 MB 时滚动,与控制台输出一样,默认记录 ERROR 级别、WARN 级别和 INFO 级别的消息。
Note:日志属性独立于实际日志记录基础设施。因此,特定配置键(例如 Logback 的
logback.configurationFile)不由 Spring Boot 管理。
4.4. 文件滚动
如果您使用 Logback,可以使用 application.properties 或 application.yaml 文件微调日志滚动设置。对于所有其他日志记录系统,您需要直接自己配置滚动设置(例如,如果您使用 Log4J2,则可以添加 log4j2.xml 或 log4j2-spring.xml 文件)。
支持以下滚动策略属性:
| 名称 | 描述 |
|---|---|
logging.logback.rollingpolicy.file-name-pattern | 用于创建日志存档的文件名模式。 |
logging.logback.rollingpolicy.clean-history-on-start | 如果应在应用程序启动时进行日志存档清理。 |
logging.logback.rollingpolicy.max-file-size | 日志文件在存档之前的最大大小。 |
logging.logback.rollingpolicy.total-size-cap | 日志存档可以占用的最大大小,然后被删除。 |
logging.logback.rollingpolicy.max-history | 要保留的最大存档日志文件数(默认为 7)。 |
4.5. 日志级别
所有支持的日志记录系统都可以在 Spring Environment 中(例如,在 application.properties 中)使用 logging.level.<logger-name>=<level> 设置日志记录器级别,其中 level 是 TRACE、DEBUG、INFO、WARN、ERROR、FATAL 或 OFF。可以使用 logging.level.root 配置 root 日志记录器。
以下示例显示了 application.properties 中的潜在日志记录设置:
Properties
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error还可以使用环境变量设置日志记录级别。例如,LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG 将 org.springframework.web 设置为 DEBUG。
Tip:上述方法仅适用于包级日志记录。由于放松绑定始终将环境变量转换为小写,因此无法以这种方式配置单个类的日志记录。如果需要为类配置日志记录,可以使用
SPRING_APPLICATION_JSON变量。
4.6. 日志组
通常,能够将相关日志记录器组合在一起以便同时配置它们是很有用的。例如,您可能经常更改所有 Tomcat 相关日志记录器的日志记录级别,但您无法轻松记住顶级包。
为了帮助解决这个问题,Spring Boot 允许您在 Spring Environment 中定义日志记录组。例如,以下是如何通过将其添加到 application.properties 来定义 “tomcat” 组:
Properties
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat定义后,您可以使用一行更改组中所有日志记录器的级别:
Properties
logging.level.tomcat=traceSpring Boot 包含以下预定义的日志记录组,可以开箱即用:
| 名称 | 日志记录器 |
|---|---|
| web | org.springframework.core.codec, org.springframework.http, org.springframework.web, org.springframework.boot.actuate.endpoint.web, org.springframework.boot.web.servlet.ServletContextInitializerBeans |
| sql | org.springframework.jdbc.core, org.hibernate.SQL, org.jooq.tools.LoggerListener |
4.7. 使用日志关闭钩子
为了在应用程序终止时释放日志记录资源,提供了一个关闭钩子,该钩子将在 JVM 退出时触发日志系统清理。除非您的应用程序作为 war 文件部署,否则会自动注册此关闭钩子。如果您的应用程序具有复杂的上下文层次结构,则关闭钩子可能无法满足您的需求。如果不满足,请禁用关闭钩子并直接研究底层日志记录系统提供的选项。例如,Logback 提供上下文选择器,它允许为每个日志记录器创建自己的上下文。您可以使用 logging.register-shutdown-hook 属性禁用关闭钩子。将其设置为 false 将禁用注册。您可以在 application.properties 或 application.yaml 文件中设置该属性:
Properties
logging.register-shutdown-hook=false4.8. 自定义日志配置
可以通过在类路径的根目录或在以下 Spring Environment 属性指定的位置提供适当的配置文件来激活各种日志记录系统:logging.config。
您可以通过使用 org.springframework.boot.logging.LoggingSystem 系统属性强制 Spring Boot 使用特定的日志记录系统。值应为 LoggingSystem 实现的完全限定类名。您还可以通过使用值 none 完全禁用 Spring Boot 的日志记录配置。
Tip:由于日志记录在
ApplicationContext创建之前初始化,因此无法从 Spring@Configuration文件中的@PropertySources控制日志记录。控制日志记录系统或完全禁用它的唯一方法是通过系统属性。
根据您的日志记录系统,将加载以下文件:
| 日志记录系统 | 自定义 |
|---|---|
| Logback | logback-spring.xml、logback-spring.groovy、logback.xml 或 logback.groovy |
| Log4j2 | log4j2-spring.xml 或 log4j2.xml |
| JDK (Java Util Logging) | logging.properties |
Tip:如果可能,建议您使用日志记录配置的
-spring变体(例如,logback-spring.xml而不是logback.xml)。如果您使用标准配置位置,Spring 无法完全控制日志初始化。
Warning:Java Util Logging 存在已知的类加载问题,这些问题会导致从 “可执行 jar” 运行时出现问题。建议您尽量避免使用它。
为了帮助自定义,将从 Spring Environment 传输其他属性到系统属性。这允许属性被日志记录系统配置消耗。例如,在 application.properties 中设置 logging.file.name 或将 LOGGING_FILE_NAME 作为环境变量将导致设置 LOG_FILE 系统属性。描述传输的属性的表格如下:
| Spring Environment | 系统属性 | 注释 |
|---|---|---|
logging.exception-conversion-word | LOG_EXCEPTION_CONVERSION_WORD | 记录异常时使用的转换字。 |
logging.file.name | LOG_FILE | 如果定义,则在默认日志配置中使用。 |
logging.file.path | LOG_PATH | 如果定义,则在默认日志配置中使用。 |
logging.pattern.console | CONSOLE_LOG_PATTERN | 控制台(stdout)上使用的日志模式。 |
logging.pattern.dateformat | LOG_DATEFORMAT_PATTERN | 日志日期格式的附加器模式。 |
logging.charset.console | CONSOLE_LOG_CHARSET | 控制台日志记录的字符集。 |
logging.pattern.file | FILE_LOG_PATTERN | 文件中使用的日志模式(如果启用了 LOG_FILE)。 |
logging.charset.file | FILE_LOG_CHARSET | 文件日志记录的字符集(如果启用了 LOG_FILE)。 |
logging.pattern.level | LOG_LEVEL_PATTERN | 渲染日志级别时使用的格式(默认为 %5p)。 |
PID | PID | 当前进程 ID(如果可能,则发现,并且在未作为操作系统环境变量定义时)。 |
如果您使用 Logback,还会传输以下属性:
| Spring Environment | 系统属性 | 注释 |
|---|---|---|
logging.logback.rollingpolicy.file-name-pattern | LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN | 滚动日志文件名的模式(默认为 ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz)。 |
logging.logback.rollingpolicy.clean-history-on-start | LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START | 是否在启动时清理存档日志文件。 |
logging.logback.rollingpolicy.max-file-size | LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE | 最大日志文件大小。 |
logging.logback.rollingpolicy.total-size-cap | LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP | 要保留的日志备份的总大小。 |
logging.logback.rollingpolicy.max-history | LOGBACK_ROLLINGPOLICY_MAX_HISTORY | 要保留的最大存档日志文件数。 |
所有支持的日志记录系统都可以在解析其配置文件时查询系统属性。请参阅 spring-boot.jar 中的默认配置示例:
Note:如果您想在日志属性中使用占位符,应使用 Spring Boot 的语法,而不是底层框架的语法。特别是,如果您使用 Logback,应使用
:作为属性名称和其默认值之间的分隔符,而不是使用:-。
Note
您可以通过仅覆盖
LOG_LEVEL_PATTERN(或 Logback 的logging.pattern.level)将 MDC 和其他临时内容添加到日志行。例如,如果您使用logging.pattern.level=user:%X{user} %5p,则默认日志格式包含 “user” 的 MDC 条目(如果存在),如下例所示。Text2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request
4.9. Logback 扩展
Spring Boot 包含一些 Logback 扩展,可以帮助进行高级配置。您可以在 logback-spring.xml 配置文件中使用这些扩展。
Tip:由于标准
logback.xml配置文件加载得太早,因此无法在其中使用扩展。您需要使用logback-spring.xml或定义logging.config属性。
Warning:扩展不能与 Logback 的配置扫描一起使用。如果您尝试这样做,更改配置文件将导致类似于以下内容的错误之一被记录:
Text
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]4.9.1. 特定配置文件的配置
<springProfile> 标签允许您可选地包含或排除配置部分,具体取决于活动的 Spring 配置文件。配置文件部分在 <configuration> 元素中的任何位置都受支持。使用 name 属性指定接受配置的配置文件。<springProfile> 标签可以包含配置文件名称(例如 staging)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如 production & (eu-central | eu-west)。请参阅参考指南以获取更多详细信息。以下列表显示了三个示例配置文件:
XML
<springProfile name="staging">
<!-- 当 “staging” 配置文件活动时启用的配置 -->
</springProfile>
<springProfile name="dev | staging">
<!-- 当 “dev” 或 “staging” 配置文件活动时启用的配置 -->
</springProfile>
<springProfile name="!production">
<!-- 当 “production” 配置文件不活动时启用的配置 -->
</springProfile>4.9.2. 环境属性
<springProperty> 标签允许您公开 Spring Environment 中的属性以供 Logback 使用。这样做可能很有用,如果您想在 Logback 配置中访问 application.properties 文件中的值。该标签的工作方式类似于 Logback 的标准 <property> 标签。但是,您指定属性的 source(来自 Environment),而不是直接 value。如果您需要将属性存储在 local 范围之外的其他位置,可以使用 scope 属性。如果您需要一个回退值(以防属性未在 Environment 中设置),可以使用 defaultValue 属性。以下示例显示了如何公开属性以供 Logback 使用:
XML
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender>Tip:
source必须以 kebab 大小写(例如my.property-name)指定。但是,可以使用放松的规则将属性添加到Environment。
5. 国际化
Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户需求。默认情况下,Spring Boot 会在类路径根目录查找 messages 资源包的存在。
Tip:自动配置在配置的资源包的默认属性文件可用时(默认为
messages.properties)应用。如果您的资源包仅包含特定语言的属性文件,则需要添加默认文件。如果没有找到与任何配置的基本名称匹配的属性文件,则不会有自动配置的MessageSource。
资源包的基本名称以及其他几个属性可以使用 spring.messages 命名空间进行配置,如下例所示:
Properties
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=falseNote:
spring.messages.basename支持逗号分隔的位置列表,可以是包限定符或从类路径根解析的资源。
请参阅 MessageSourceProperties 以获取更多支持的选项。
6. 面向切面的编程
Spring Boot 提供了面向切面编程(AOP)的自动配置。您可以在 Spring Framework 参考文档中了解有关 Spring 中 AOP 的更多信息。
默认情况下,Spring Boot 的自动配置会配置 Spring AOP 使用 CGLib 代理。要使用 JDK 代理,请将 configprop:spring.aop.proxy-target-class 设置为 false。
如果 AspectJ 在类路径上,Spring Boot 的自动配置将自动启用 AspectJ 自动代理,因此不需要 @EnableAspectJAutoProxy。
7. JSON
Spring Boot 提供与三个 JSON 映射库的集成:
- Gson
- Jackson
- JSON-B
Jackson 是首选和默认库。
7.1. Jackson
为 Jackson 提供了自动配置,并且 Jackson 是 spring-boot-starter-json 的一部分。当 Jackson 在类路径上时,会自动配置 ObjectMapper bean。提供了几个配置属性,用于自定义 ObjectMapper 的配置。
7.1.1. 自定义序列化器和反序列化器
如果您使用 Jackson 序列化和反序列化 JSON 数据,您可能希望编写自己的 JsonSerializer 和 JsonDeserializer 类。自定义序列化器通常通过模块注册到 Jackson,但 Spring Boot 提供了替代的 @JsonComponent 注解,使直接注册 Spring Beans 更加容易。
您可以直接在 JsonSerializer、JsonDeserializer 或 KeyDeserializer 实现上使用 @JsonComponent 注解。您还可以在包含序列化器/反序列化器作为内部类的类上使用它,如下例所示:
Java
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonSerializer<MyObject> {
@Override
public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
jgen.writeStartObject();
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
jgen.writeEndObject();
}
}
public static class Deserializer extends JsonDeserializer<MyObject> {
@Override
public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode tree = codec.readTree(jsonParser);
String name = tree.get("name").textValue();
int age = tree.get("age").intValue();
return new MyObject(name, age);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ApplicationContext 中的所有 @JsonComponent bean 都会自动注册到 Jackson。因为 @JsonComponent 被元注解为 @Component,所以通常的组件扫描规则适用。
Spring Boot 还提供了 JsonObjectSerializer 和 JsonObjectDeserializer 基类,这些基类在序列化对象时提供了有用的替代方案。请参阅 Javadoc 中的 JsonObjectSerializer 和 JsonObjectDeserializer 以获取详细信息。
上例可以重写为使用 JsonObjectSerializer/JsonObjectDeserializer,如下所示:
Java
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.boot.jackson.JsonObjectSerializer;
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonObjectSerializer<MyObject> {
@Override
protected void serializeObject(MyObject value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
}
}
public static class Deserializer extends JsonObjectDeserializer<MyObject> {
@Override
protected MyObject deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec,
JsonNode tree) throws IOException {
String name = nullSafeValue(tree.get("name"), String.class);
int age = nullSafeValue(tree.get("age"), Integer.class);
return new MyObject(name, age);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
7.1.2. Mixins
Jackson 支持混合,可以用来将额外的注解混合到已经在目标类上声明的注解中。Spring Boot 的 Jackson 自动配置将扫描您的应用程序包中带有 @JsonMixin 注解的类,并将它们注册到自动配置的 ObjectMapper 中。注册由 Spring Boot 的 JsonMixinModule 执行。
7.2. Gson
为 Gson 提供了自动配置。当 Gson 在类路径上时,会自动配置 Gson bean。提供了几个 spring.gson.* 配置属性,用于自定义配置。要获得更多控制权,可以使用一个或多个 GsonBuilderCustomizer bean。
7.3. JSON-B
为 JSON-B 提供了自动配置。当 JSON-B API 和实现在类路径上时,将自动配置 Jsonb bean。首选的 JSON-B 实现是 Apache Johnzon,为其提供了依赖管理。
8. 任务执行和调度
在上下文中没有 Executor bean 的情况下,Spring Boot 会自动配置一个具有合理默认值的 ThreadPoolTaskExecutor,可以自动与异步任务执行(@EnableAsync)和 Spring MVC 异步请求处理相关联。
Note
如果您在上下文中定义了自定义的
Executor,则常规任务执行(即@EnableAsync)将透明地使用它,但 Spring MVC 支持将不会配置,因为它需要一个AsyncTaskExecutor实现(名为applicationTaskExecutor)。根据您的目标安排,您可以将您的Executor更改为ThreadPoolTaskExecutor,或者定义一个ThreadPoolTaskExecutor和一个包装您的自定义Executor的AsyncConfigurer。自动配置的
TaskExecutorBuilder允许您轻松创建与自动配置默认执行的实例。
线程池使用 8 个核心线程,可以根据负载增加和减少。这些默认设置可以使用 spring.task.execution 命名空间进行微调,如下例所示:
Properties
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s这会将线程池更改为使用有界队列,以便当队列满(100 个任务)时,线程池增加到最大 16 个线程。线程池的缩减更加激进,因为当线程空闲 10 秒(而不是默认的 60 秒)时,线程会被回收。
如果需要与计划任务执行(例如使用 @EnableScheduling)相关联,还可以自动配置 ThreadPoolTaskScheduler。线程池默认使用一个线程,其设置可以使用 spring.task.scheduling 命名空间进行微调,如下例所示:
Properties
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2如果需要创建自定义执行器或调度器,则会在上下文中提供 TaskExecutorBuilder bean 和 TaskSchedulerBuilder bean。