Spring Boot 应用程序
本节包含与 Spring Boot 应用程序直接相关的主题。
创建自己的 FailureAnalyzer
FailureAnalyzer
是一种很好的方式,可以在启动时拦截异常并将其转换为人类可读的消息,包装在 FailureAnalysis
中。
Spring Boot 为应用程序上下文相关异常、JSR-303 验证等提供了这样的分析器。
您也可以创建自己的分析器。
AbstractFailureAnalyzer
是 FailureAnalyzer
的一个便捷扩展,它检查要处理的异常中是否存在指定的异常类型。
您可以从中扩展,这样您的实现只有在异常实际存在时才有机会处理它。
如果由于任何原因您无法处理异常,返回 null
以让其他实现有机会处理该异常。
FailureAnalyzer
实现必须在 META-INF/spring.factories
中注册。
以下示例注册了 ProjectConstraintViolationFailureAnalyzer
:
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
注意:如果您需要访问 BeanFactory
或 Environment
,请在您的 FailureAnalyzer
实现中将它们声明为构造函数参数。
自动配置故障排除
Spring Boot 自动配置会尽力"做正确的事情",但有时事情会失败,而且很难判断原因。
在任何 Spring Boot ApplicationContext
中都有一个非常有用的 ConditionEvaluationReport
。
如果您启用 DEBUG
日志输出,您可以看到它。
如果您使用 spring-boot-actuator
(请参阅 Actuator 部分),还有一个 conditions
端点以 JSON 格式呈现报告。
使用该端点来调试应用程序,并查看 Spring Boot 在运行时添加了哪些功能(以及哪些功能未添加)。
通过查看源代码和 API 文档可以回答更多问题。 阅读代码时,请记住以下经验法则:
-
查找名为
*AutoConfiguration
的类并阅读其源代码。 特别注意@Conditional*
注解,以了解它们启用了哪些功能以及何时启用。 在命令行中添加--debug
或系统属性-Ddebug
以在控制台上获取应用程序中所有自动配置决策的日志。 在启用了 actuator 的运行应用程序中,查看conditions
端点(/actuator/conditions
或 JMX 等效项)以获取相同的信息。 -
查找
@ConfigurationProperties
的类(如ServerProperties
)并从那里读取可用的外部配置选项。@ConfigurationProperties
注解有一个name
属性,它作为外部属性的前缀。 因此,ServerProperties
有prefix="server"
,其配置属性为server.port
、server.address
等。 在启用了 actuator 的运行应用程序中,查看configprops
端点。 -
查找
Binder
上bind
方法的使用,以宽松的方式从Environment
中显式提取配置值。 它通常与前缀一起使用。 -
查找
@Value
注解,它们直接绑定到Environment
。 -
查找
@ConditionalOnExpression
注解,它们根据 SpEL 表达式切换功能的开启和关闭,通常使用从Environment
解析的占位符进行评估。
在启动前自定义 Environment 或 ApplicationContext
SpringApplication
有 ApplicationListener
和 ApplicationContextInitializer
实现,用于对上下文或环境应用自定义。
Spring Boot 从 META-INF/spring.factories
加载许多这样的自定义以供内部使用。
有多种方式可以注册额外的自定义:
-
以编程方式,按应用程序,通过在运行
SpringApplication
之前调用其addListeners
和addInitializers
方法。 -
以声明方式,为所有应用程序,通过添加
META-INF/spring.factories
并打包一个所有应用程序都用作库的 jar 文件。
SpringApplication
向监听器发送一些特殊的 ApplicationEvents
(有些甚至在创建上下文之前),然后还注册监听器以监听 ApplicationContext
发布的事件。
有关完整列表,请参阅"Spring Boot 功能"部分中的 应用程序事件和监听器。
还可以通过使用 EnvironmentPostProcessor
在刷新应用程序上下文之前自定义 Environment
。
每个实现都应在 META-INF/spring.factories
中注册,如下例所示:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
该实现可以加载任意文件并将它们添加到 Environment
。
例如,以下示例从类路径加载 YAML 配置文件:
-
Java
-
Kotlin
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
}
private PropertySource<?> loadYaml(Resource path) {
Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
try {
return this.loader.load("custom-resource", path).get(0);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
}
}
}
import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.boot.env.YamlPropertySourceLoader
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.PropertySource
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.util.Assert
import java.io.IOException
class MyEnvironmentPostProcessor : EnvironmentPostProcessor {
private val loader = YamlPropertySourceLoader()
override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
val path: Resource = ClassPathResource("com/example/myapp/config.yml")
val propertySource = loadYaml(path)
environment.propertySources.addLast(propertySource)
}
private fun loadYaml(path: Resource): PropertySource<*> {
Assert.isTrue(path.exists()) { "Resource $path does not exist" }
return try {
loader.load("custom-resource", path)[0]
} catch (ex: IOException) {
throw IllegalStateException("Failed to load yaml configuration from $path", ex)
}
}
}
提示:Environment
已经准备好了 Spring Boot 默认加载的所有常用属性源。
因此,可以从环境中获取文件的位置。
前面的示例在列表末尾添加了 custom-resource
属性源,以便在任何其他常用位置定义的键优先。
自定义实现可以定义另一个顺序。
警告:虽然在您的 @SpringBootApplication
上使用 @PropertySource
可能看起来是在 Environment
中加载自定义资源的便捷方式,但我们不建议这样做。
这些属性源直到应用程序上下文被刷新时才会添加到 Environment
。
这对于在刷新开始之前读取的某些属性(如 logging.*
和 spring.main.*
)来说太晚了。
构建 ApplicationContext 层次结构(添加父上下文或根上下文)
您可以使用 SpringApplicationBuilder
类来创建父/子 ApplicationContext
层次结构。
有关更多信息,请参阅"Spring Boot 功能"部分中的 流式构建器 API。
创建非 Web 应用程序
并非所有 Spring 应用程序都必须是 Web 应用程序(或 Web 服务)。
如果您想在 main
方法中执行一些代码,但还要引导 Spring 应用程序来设置要使用的基础设施,您可以使用 Spring Boot 的 SpringApplication
功能。
SpringApplication
会根据它认为是否需要 Web 应用程序来更改其 ApplicationContext
类。
您可以通过将服务器相关依赖项(如 servlet API)从类路径中移除来帮助它。
如果您不能这样做(例如,如果您从同一个代码库运行两个应用程序),那么您可以在 SpringApplication
实例上显式调用 setWebApplicationType(WebApplicationType.NONE)
或设置 applicationContextClass
属性(通过 Java API 或外部属性)。
您想要作为业务逻辑运行的应用程序代码可以实现为 CommandLineRunner
并作为 @Bean
定义放入上下文中。