Appearance
7.核心功能
本节深入探讨 Spring Boot 的详细信息。 在这里,您可以了解您可能想要使用和自定义的主要功能。 如果您还没有这样做,则可能需要阅读“入门”和“使用 Spring Boot 进行开发”部分,以便您对基础知识有良好的了解。
7.1 SpringApplication 应用程序
SpringApplication
类提供了一种便捷的方法来引导从方法启动的 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);
}
}
kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
当您的应用程序启动时,您应该会看到类似于以下输出的内容:
txt
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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
日志记录消息,包括一些相关的启动详细信息,例如启动应用程序的用户。 如果您需要的日志级别不是 INFO
,则可以设置它,如 Log Levels 中所述。 应用程序版本是使用主应用程序类的包中的实现版本确定的。 可以通过设置spring.main.log-startup-info
为false
来关闭启动信息记录。 这还将关闭应用程序活动配置文件的日志记录
TIP
要在启动期间添加其他日志记录,可以在 SpringApplication
的子类中覆盖logStartupInfo(boolean)
。
7.1.1 启动失败
如果您的应用程序无法启动,注册FailureAnalyzers
后将有机会提供专用错误消息和解决问题的具体操作。 例如,如果您在 端口8080
上启动一个 Web 应用程序,并且该端口已在使用中,您应该会看到类似于以下消息的内容
txt
***************************
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
实现,您可以添加自己的实现
如果没有故障分析器能够处理异常,您仍然可以显示完整的条件报告,以更好地了解出了什么问题。 为此,您需要为org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
启用 debug 属性或启用debug日志
例如,如果您使用java -jar
运行应用程序,则可以按如下方式启用debug
属性
shell
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
7.1.2 延迟初始化
SpringApplication
允许延迟初始化应用程序。 启用延迟初始化后,将根据需要创建 bean,而不是在应用程序启动期间创建 bean。 因此,启用延迟初始化可以减少应用程序启动所需的时间。 在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 bean 在收到 HTTP 请求之前不会初始化。
延迟初始化的一个缺点是,它可能会延迟发现应用程序的问题。 如果延迟初始化了配置错误的 bean,则在启动期间将不再发生失败,并且只有在初始化 bean 时问题才会变得明显。 还必须注意确保 JVM 具有足够的内存来容纳应用程序的所有 bean,而不仅仅是在启动期间初始化的 bean。 由于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。
可以使用SpringApplicationBuilder
的lazyInitialization
方法 或 SpringApplication
的setLazyInitialization
方法以编程方式启用延迟初始化。 或者,可以使用以下示例中所示的spring.main.lazy-initialization
属性启用它:
properties
spring.main.lazy-initialization=true
yaml
spring:
main:
lazy-initialization: true
TIP
如果要对某些 bean 禁用延迟初始化,同时对应用程序的其余部分使用延迟初始化,则可以使用注解@Lazy(false)
将其 lazy 属性显式设置为 false。
7.1.3 自定义 Banner
可以通过将文件banner.txt
添加到 Classpath 或将属性spring.banner.location
设置为此类文件的位置来更改启动时打印的标题。 如果文件的编码不是 UTF-8,则可以设置spring.banner.charset
. 除了文本文件之外,还可以将 banner.gif
、banner.jpg
或 banner.png
image 文件添加到 Classpath 中或设置属性spring.banner.image.location
。 图像将转换为 ASCII 艺术表示形式,并打印在任何文本横幅上方。
在banner.txt
文件中,您可以使用Environment
中的任何可用键以及以下任何占位符:
表 4. Banner 变量
变量 | 描述 |
---|---|
$ | 应用程序中MANIFEST.MF 声明的应用程序版本号 。 例如,Implementation-Version: 1.0 打印为1.0 。 |
$ | 应用程序的版本号,在MANIFEST.MF 中声明并格式化为显示(用括号括起来,前缀为v )。 例如v(v1.0) 。 |
$ | 您正在使用的 Spring Boot 版本。 例如2.7.18 。 |
$ | 您正在使用的 Spring Boot 版本,格式化为显示(用括号括起来,前缀为v )。 例如(v2.7.18) 。 |
${Ansi.NAME} (或 ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}) | 其中NAME 是 ANSI 转义码的名称。 有关详细信息,请参阅 AnsiPropertySource。 |
$ | 应用程序中MANIFEST.MF 声明的应用程序标题。 例如Implementation-Title: MyApp ,打印为MyApp . |
TIP
如果要以编程方式生成Banner,可以使用SpringApplication.setBanner(…)
方法。 使用org.springframework.boot.Banner
接口并实现您自己的printBanner()
方法
您还可以使用spring.main.banner-mode
属性来确定Banner是否必须打印在System.out (console)
上、发送到配置的记录器 (log
) 或根本不生成 (off
)
打印的Banner将注册为以下名称下的单例 Bean:springBootBanner
TIP
application.title
,application.version
和application.formatted-version
属性仅在您通过Spring Boot 启动器使用java -jar
或java -cp
时可用。 如果您运行的是解压缩的 jar 并使用java -cp <classpath> <mainclass>
开始时,这些属性时不解析的。
要使用这些application.
属性,请使用java -jar
启动打包的 jar,或使用java org.springframework.boot.loader.JarLauncher
启动未压缩的 jar。 这将在构建类路径和启动程序之前初始化application.
banner属性。
7.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);
}
}
kotlin
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
}
}
TIP
传递给SpringApplication
的构造函数参数是 Spring bean 的配置源。 在大多数情况下,这些是对类@Configuration
的引用,但它们也可以是直接引用@Component
类
也可以使用application.properties
文件配置SpringApplication
。 有关详细信息,请参阅外部化配置。
有关配置选项的完整列表,请参阅 SpringApplication Javadoc。
7.1.5 Fluent Builder API
如果您需要构建ApplicationContext
层次结构(具有父/子关系的多个上下文),或者您更喜欢使用“Fluent”构建器 API,则可以使用SpringApplicationBuilder
SpringApplicationBuilder
允许您将多个方法调用和包含以及允许您创建层次结构的parent
和child
方法链接在一起,如以下示例所示:
java
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
kotlin
SpringApplicationBuilder()
.sources(Parent::class.java)
.child(Application::class.java)
.bannerMode(Banner.Mode.OFF)
.run(*args)
TIP
创建ApplicationContext
层次结构时存在一些限制。 例如,Web 组件必须包含在子上下文中,并且父上下文和子上下文都使用相同的Environment
组件。 有关完整详细信息,请参见 SpringApplicationBuilder Javadoc。
7.1.6 应用程序可用性
在平台上部署时,应用程序可以使用 Kubernetes 探针等基础设施向平台提供有关其可用性的信息。 Spring Boot 包括对常用的“liveness”和“readiness”可用性状态的开箱即用支持。 如果您使用的是 Spring Boot 的“actuator”支持,则这些状态将作为运行状况端点组公开。
此外,您还可以通过将接口ApplicationAvailability
注入到您自己的 bean 中来获取可用性状态
活动状态
应用程序的 “Liveness” 状态表明其内部状态是否允许它正常工作,或者如果当前出现故障,则自行恢复。 损坏的 “Liveness” 状态意味着应用程序处于无法恢复的状态,基础设施应重新启动应用程序。
TIP
通常,“Liveness” 状态不应基于外部检查,例如 Health 检查。 如果是这样,则失败的外部系统(数据库、Web API、外部缓存)将在整个平台上触发大规模重启和级联故障。
Spring Boot 应用程序的内部状态主要由 Spring ApplicationContext
表示。 如果应用程序上下文已成功启动,则 Spring Boot 假定应用程序处于有效状态。 一旦刷新了上下文,应用程序就被视为活动了,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
就绪状态
应用程序的 “Readiness” 状态表明应用程序是否已准备好处理流量。 失败的 “Readiness” 状态告诉平台它暂时不应将流量路由到应用程序。 这通常发生在启动期间、处理组件CommandLineRunner
和ApplicationRunner
时,或者如果应用程序决定太忙而无法进行其他流量,则随时发生。
一旦调用了应用程序和命令行运行器,应用程序就被视为准备就绪,请参阅Spring Boot 应用程序生命周期和相关应用程序事件。
TIP
预期在启动期间运行的任务应由 CommandLineRunner
和ApplicationRunner
组件执行,而不是使用 Spring 组件生命周期回调,例如@PostConstruct
管理应用程序可用性状态
应用程序组件可以随时通过注入接口ApplicationAvailability
并对其调用方法来检索当前可用性状态。 更常见的是,应用程序需要侦听状态更新或更新应用程序的状态
例如,我们可以将应用程序的 “Readiness” 状态导出到一个文件中,以便 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;
}
}
}
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.ReadinessState
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
@Component
class MyReadinessStateExporter {
@EventListener
fun onStateChange(event: AvailabilityChangeEvent<ReadinessState?>) {
when (event.state) {
ReadinessState.ACCEPTING_TRAFFIC -> {
// create file /tmp/healthy
}
ReadinessState.REFUSING_TRAFFIC -> {
// remove file /tmp/healthy
}
else -> {
// ...
}
}
}
}
当应用程序中断且无法恢复时,我们还可以更新应用程序的状态:
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);
}
}
}
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.LivenessState
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component
@Component
class MyLocalCacheVerifier(private val eventPublisher: ApplicationEventPublisher) {
fun checkLocalCache() {
try {
// ...
} catch (ex: CacheCompletelyBrokenException) {
AvailabilityChangeEvent.publish(eventPublisher, ex, LivenessState.BROKEN)
}
}
}
Spring Boot 通过 Actuator Health Endpoints 为 “Liveness” 和 “Readiness” 提供 Kubernetes HTTP 探测。 您可以在专用部分中获取有关在 Kubernetes 上部署 Spring Boot 应用程序的更多指导。
7.1.7 应用程序事件和侦听器
除了通常的 Spring Framework 事件(例如ContextRefreshedEvent)之外,SpringApplication
还会发送一些其他应用程序事件。
TIP
某些事件实际上是在创建ApplicationContext
之前触发的,因此您无法在这些事件上将侦听器注册为@Bean
。 您可以使用 SpringApplication.addListeners(…)
或 SpringApplicationBuilder.listeners(…)
注册它们
如果您希望自动注册这些侦听器,而不管应用程序是如何创建的,您都可以将文件META-INF/spring.factories
添加到项目中并使用org.springframework.context.ApplicationListener
引用您的侦听器,如以下示例所示:
properties
org.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件在应用程序运行时按以下顺序发送:
ApplicationStartingEvent
在运行开始时发送,但在任何处理之前发送,侦听器和初始值设定项的注册除外。- 当
Environment
要在上下文中使用的 已知但在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent
。 - 当准备好
ApplicationContext
并调用ApplicationContextInitializers时,但在加载任何 bean 定义之前,将发送ApplicationEnvironmentPreparedEvent
- 在刷新开始之前,但在加载 Bean 定义之后发送
ApplicationPreparedEvent
- 在刷新上下文之后,但在调用任何应用程序和命令行运行程序之前发送
ApplicationStartedEvent
AvailabilityChangeEvent
紧随LivenessState.CORRECT
其后发送,以指示应用程序被视为实时应用程序。- 在调用任何应用程序和命令行运行程序后发送
ApplicationReadyEvent
AvailabilityChangeEvent
紧随ReadinessState.ACCEPTING_TRAFFIC
其后发送,以指示应用程序已准备好为请求提供服务- 如果启动时出现异常,则发送
ApplicationFailedEvent
上面的SpringApplicationEvents
列表仅包含绑定到 SpringApplication
. 除此之外,以下事件还会在ApplicationPreparedEvent
之后 和 ApplicationStartedEvent
之前发布 :
WebServerInitializedEvent
在WebServer
准备就绪后发送。ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是 servlet 和 reactive 变体。- 刷新
ApplicationContext
时发送ContextRefreshedEvent
TIP
您通常不需要使用应用程序事件,但知道它们存在会很方便。 在内部, Spring Boot 使用事件来处理各种任务。
TIP
事件侦听器不应运行可能很长的任务,因为它们默认在同一线程中执行。 请考虑改用应用程序和命令行运行程序。
应用程序事件是使用 Spring Framework 的事件发布机制发送的。 此机制的一部分可确保发布到子上下文中的侦听器的事件也发布到任何祖先上下文中的侦听器。 因此,如果您的应用程序使用SpringApplication
实例层次结构,则侦听器可能会接收相同类型应用程序事件的多个实例。
要使侦听器能够区分其上下文的事件和后代上下文的事件,它应请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。 可以通过实现ApplicationContextAware
来注入上下文,或者,如果侦听器是 bean,则通过使用 @Autowired
7.1.8 Web 环境
SpringApplication
尝试代表您创建正确的ApplicationContext
类型。 用于确定WebApplicationType
的算法如下:
- 如果存在 Spring MVC,则使用
AnnotationConfigServletWebServerApplicationContext
- 如果 Spring MVC 不存在而 Spring WebFlux 存在,则使用
AnnotationConfigReactiveWebServerApplicationContext
- 否则,使用
AnnotationConfigApplicationContext
这意味着,如果你在同一个应用程序中使用 Spring MVC 和 Spring WebFlux 中的新版本,则默认情况下将使用 Spring MVC。 您可以通过调用setWebApplicationType(WebApplicationType)
.
还可以通过调用setApplicationContextFactory(…)
.
TIP
在 JUnit 测试中使用时,通常需要调用setWebApplicationType(WebApplicationType.NONE)
。
7.1.9 访问应用程序参数
如果需要访问传递给SpringApplication.run(…)
的应用程序参数,则可以注入一个org.springframework.boot.ApplicationArguments
bean。 ApplicationArguments
接口提供对原始参数String[]
以及 parsed option
和non-option
arguments 的访问,如以下示例所示:
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"]
}
}
Kotlin
import org.springframework.boot.ApplicationArguments
import org.springframework.stereotype.Component
@Component
class MyBean(args: ApplicationArguments) {
init {
val debug = args.containsOption("debug")
val files = args.nonOptionArgs
if (debug) {
println(files)
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
TIP
Spring Boot 还向 Spring Environment
注册了一个CommandLinePropertySource
。 这还允许您使用@Value
注入单个应用程序参数。
7.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...
}
}
Kotlin
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component
@Component
class MyCommandLineRunner : CommandLineRunner {
override fun run(vararg args: String) {
// Do something...
}
}
如果定义了多个 CommandLineRunner
或ApplicationRunner
bean,并且必须按特定 顺序 调用,则还可以实现org.springframework.core.Ordered
接口或使用 org.springframework.core.annotation.Order
注解
7.1.11 应用程序退出
每个 SpringApplication
都会向 JVM 注册一个 shutdown 钩子,以确保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)));
}
}
Kotlin
import org.springframework.boot.ExitCodeGenerator
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import kotlin.system.exitProcess
@SpringBootApplication
class MyApplication {
@Bean
fun exitCodeGenerator() = ExitCodeGenerator { 42 }
}
fun main(args: Array<String>) {
exitProcess(SpringApplication.exit(
runApplication<MyApplication>(*args)))
}
此外,ExitCodeGenerator
接口可能由异常实现。 当遇到此类异常时, Spring Boot 返回已实现的getExitCode()
方法提供的退出代码。
如果有多个 ExitCodeGenerator
,则使用生成的第一个非零退出代码。 要控制生成器的调用顺序,请另外实现org.springframework.core.Ordered
接口或使用org.springframework.core.annotation.Order
注解。
7.1.12 管理员功能
可以通过指定spring.application.admin.enabled
属性为应用程序启用与管理员相关的功能。 这会在 MBeanServer
上公开 SpringApplicationAdminMXBean。 您可以使用此功能远程管理 Spring Boot 应用程序。 此功能对于任何服务包装器实现也很有用。
TIP
如果您想知道应用程序正在哪个 HTTP 端口上运行,请获取键为local.server.port
7.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);
}
}
kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
applicationStartup = BufferingApplicationStartup(2048)
}
}
第一个可用的实现FlightRecorderApplicationStartup
由 Spring Framework 提供。 它将特定于 Spring 的启动事件添加到 Java Flight Recorder 会话中,用于分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GC、类加载等)相关联。 配置后,您可以通过在启用 Flight Recorder 的情况下运行应用程序来记录数据:
shell
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
Spring Boot 附带了BufferingApplicationStartup
变体;此实现用于缓冲启动步骤并将其排空到外部 Metrics 系统中。 应用程序可以在任何组件中请求 BufferingApplicationStartup
为 的 bean。
Spring Boot 还可以配置为公开一个启动端点,该端点将此信息作为 JSON 文档提供。
7.2 外部化配置
Spring Boot 允许您外部化配置,以便您可以在不同环境中使用相同的应用程序代码。 您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用 @Value
直接注入到 bean 中,通过 Spring 的Environment
抽象访问,或者通过@ConfigurationProperties
Spring Boot 使用一个非常特殊的 PropertySource
顺序,旨在允许合理地覆盖值。 后面的属性源可以覆盖前面的属性源中定义的值。 按以下顺序考虑源:
- 默认属性(通过设置
SpringApplication.setDefaultProperties
指定)。 @PropertySource
类上的@Configuration
。 请注意,在刷新应用程序上下文之前,此类属性源不会添加到Environment
。 现在配置某些属性(例如,在刷新开始之前读取的logging.*
和spring.main.*
)为时已晚。- 配置数据(例如文件
application.properties
) - 仅在
RandomValuePropertySource
中具有属性random.*
。 - OS 环境变量。
- Java 系统属性 (
System.getProperties()
) - JNDI 属性来自
java:comp/env
ServletContext init
参数。ServletConfig init
参数- 属性
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;
// ...
}
Kotlin
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
在应用程序类路径上(例如,在 jar 中),您可以有一个application.properties
文件,该文件为 name
. 在新环境中运行时,可以在 jar 外部提供一个application.properties
文件,该文件会覆盖 name
. 对于一次性测试,您可以使用特定的命令行开关(例如java -jar app.jar --name="Spring"
)。
TIP
env
和configprops
endpoints 可用于确定属性具有特定值的原因。 您可以使用这两个终端节点来诊断意外的属性值。 有关详细信息,请参阅“生产就绪功能”部分。
7.2.1 访问命令行属性
默认情况下,将任何命令行选项参数(即以 --
、 --server.port=9000
开头的参数,例如 )转换为property
并将它们添加到 SpringEnvironment
中。 如前所述,命令行属性始终优先于基于文件的属性源。
如果不希望将命令行属性添加到Environment
中,则可以使用 SpringApplication.setAddCommandLineProperties(false)
来禁用它们。
7.2.2 JSON 应用程序属性
环境变量和系统属性通常具有限制,这意味着无法使用某些属性名称。 为了帮助解决这个问题, Spring Boot 允许您将一个属性块编码为单个 JSON 结构。
当您的应用程序启动时,将解析spring.application.json
或SPRING_APPLICATION_JSON
属性并将其添加到Environment
.
例如,可以在 UN*X shell 的命令行中将SPRING_APPLICATION_JSON
属性作为环境变量提供:
shell
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,您最终my.name=test
会在 SpringEnvironment
.
相同的 JSON 也可以作为系统属性提供:
shell
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
或者,您可以使用命令行参数提供 JSON:
shell
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
如果要部署到经典 Application Server,则还可以使用名为java:comp/env/spring.application.json
的 JNDI 变量。
TIP
尽管 JSON 中的值将添加到生成的属性源中,但PropertySourcesPropertyResolver
会将null
属性视为缺失值。 这意味着 JSON 无法使用值覆盖低阶属性源中的null
属性。
7.2.3 外部应用程序属性
Spring Boot 将在应用程序启动时自动从以下位置查找并加载文件application.properties
和application.yaml
:
- 从 classpath a. 类路径根 b. classpath 下
/config
- 从当前目录 a. 当前目录 b. 当前目录中的子目录
config/
c. 子目录的直接子目录config/
该列表按优先级排序(较低项中的值将覆盖较早项的值)。 加载文件中的文档将添加PropertySources
到 Spring Environment
中。
如果您不喜欢application
作为配置文件名,则可以通过指定 spring.config.name
属性来切换到其他文件名。 例如,要查找 myproject.properties
和myproject.yaml
文件,您可以按如下方式运行应用程序:
shell
$ java -jar myproject.jar --spring.config.name=myproject
您还可以使用 spring.config.location
属性引用显式位置。 此属性接受要检查的一个或多个位置的逗号分隔列表。
以下示例显示如何指定两个不同的文件:
shell
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
TIP
如果位置是可选的,并且您不介意它们不存在,请使用前缀optional:
。
TIP
spring.config.name
、spring.config.location
和 spring.config.additional-location
用于确定必须加载哪些文件。 它们必须定义为环境属性(通常是 OS 环境变量、系统属性或命令行参数)。
如果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 知道应该如何对它们进行分组。 位置组是所有位置都被视为同一级别的位置的集合。 例如,您可能希望对所有 Classpath 位置进行分组,然后对所有外部位置进行分组。 位置组中的项目应以 分隔。 有关更多详细信息,请参阅“分析特定文件”部分中的示例。
使用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
(或您选择的任何其他 basename spring.config.name
) 中为应用程序提供默认值。 然后,可以在运行时使用位于其中一个自定义位置的不同文件覆盖这些默认值。
可选位置
默认情况下,当指定的 config 数据位置不存在时, Spring Boot 将抛出一个ConfigDataLocationNotFoundException
,并且您的应用程序将不会启动。
如果要指定位置,但不介意它并不总是存在,则可以使用前缀optional:
。 你可以将此前缀与spring.config.location
和spring.config.additional-location
属性以及[]spring.config.import
](https://docs.spring.io/spring-boot/docs/2.7.18/reference/htmlsingle/#features.external-config.files.importing)声明一起使用。
例如,值spring.config.import
为optional:file:./myconfig.properties
允许您的应用程序启动,即使文件myconfig.properties
缺失也是如此。
如果要忽略所有ConfigDataLocationNotFoundExceptions
并始终继续启动应用程序,则可以使用spring.config.on-not-found
属性。 通过SpringApplication.setDefaultProperties(…)
或者系统变量/环境变量将值设置为ignore
。
通配符位置
如果配置文件路径最后一个路径段包含*
字符,则将其视为通配符位置。 加载配置时,通配符会展开,以便同时检查直接子目录。 通配符位置在 Kubernetes 等环境中,当有多个 config 属性来源时特别有用
例如,如果您有一些 Redis 配置和一些 MySQL 配置,您可能希望将这两个配置分开,同时要求这两个配置都存在于一个application.properties
文件中。 这可能会导致两个单独的application.properties
文件挂载在不同的位置,例如/config/redis/application.properties
和/config/mysql/application.properties
。 在这种情况下,如果通配符位置为config/*/
,将导致两个文件都被处理。
默认情况下, Spring Boot 包含config/*/
在默认搜索位置中。 这意味着将搜索 jar 之外的目录的所有子目录
您可以自己将通配符位置与 spring.config.location
和spring.config.additional-location
属性一起使用。
TIP
通配符位置必须仅包含一个*
通配符位置,对于作为目录的搜索位置或作为文件的搜索位置,通配符位置必须以*/
结尾。 带有通配符的位置根据文件名的绝对路径按字母顺序排序。
TIP
通配符位置仅适用于外部目录。 不能在classpath:
位置使用通配符。
配置文件特定
除了application
属性文件,Spring Boot 还将尝试使用 application-{profile}
加载特定于配置文件的文件。 例如,如果您的应用程序激活名为prod
YAML 文件的配置文件并使用 YAML 文件,则 application.yaml
和application-prod.yaml
都将被考虑。
特定于配置文件的属性从与 application.properties
相同的位置加载,特定于配置文件的文件始终覆盖非特定文件。 如果指定了多个配置文件,则适用 last-wins 策略。 例如,如果配置文件prod,live
由属性spring.profiles.active
指定,则application-prod.properties
中的值可以被application-live.properties
中的值覆盖。
TIP
“最后获胜”策略适用于营业地点组级别。 spring.config.location
等于classpath:/cfg/,classpath:/ext/
将具有与classpath:/cfg/;classpath:/ext/
例如,继续上面的示例prod,live
,我们可能有以下文件:
txt
/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
属性文件只加载一次。 如果您已经直接导入了特定于配置文件的属性文件,则不会再次导入它。
导入其他数据
应用程序属性可以使用spring.config.import
属性从其他位置导入进一步的配置数据。 导入在被发现时进行处理,并被视为插入到声明导入的导入文档的正下方的附加文档。
例如,您的 Classpath 文件application.properties
中可能包含以下内容:
properties
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
Yaml
spring:
config:
import: "my.properties"
my:
property: "value"
这将触发当前目录中dev.properties
文件的导入(如果存在此类文件)。 导入dev.properties
的值将优先于触发导入的文件。 在上面的示例中,dev.properties
可以重新定义spring.application.name
为不同的值。
无论声明多少次,导入都只会导入一次。 在 properties/yaml 文件中的单个文档中定义导入的顺序无关紧要。 例如,下面的两个示例产生相同的结果:
properties
spring.config.import=my.properties
my.property=value
Yaml
spring:
config:
import: "my.properties"
my:
property: "value"
properties
my.property=value
spring.config.import=my.properties
Yaml
my:
property: "value"
spring:
config:
import: "my.properties"