Spring Boot 应用程序

本节包含与 Spring Boot 应用程序直接相关的主题。

创建您自己的 FailureAnalyzer

FailureAnalyzer 是在启动时拦截异常并将其转换为人类可读消息(包装在 FailureAnalysis 中)的绝佳方式。Spring Boot 为与应用程序上下文相关的异常、JSR-303 验证等提供了此类分析器。您还可以创建自己的分析器。

AbstractFailureAnalyzerFailureAnalyzer 的便捷扩展,它检查要处理的异常中是否存在指定的异常类型。您可以从此处进行扩展,以便仅在实际存在异常时,您的实现才有机会处理该异常。如果由于某种原因您无法处理异常,请返回 null 以便其他实现有机会处理该异常。

FailureAnalyzer 实现必须在 META-INF/spring.factories 中注册。以下示例注册了 ProjectConstraintViolationFailureAnalyzer

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
如果您需要访问 BeanFactoryEnvironment,请在 FailureAnalyzer 实现中将其声明为构造函数参数。

故障排除自动配置

Spring Boot 自动配置尽力“做正确的事”,但有时会失败,并且很难说出原因。

在任何 Spring Boot ApplicationContext 中都有一个非常有用的 ConditionEvaluationReport。如果启用 DEBUG 日志输出,则可以看到它。如果你使用 spring-boot-actuator(请参阅 Actuator 章节),还有一个 conditions 端点,以 JSON 呈现报告。使用该端点调试应用程序,并查看 Spring Boot 在运行时添加了哪些功能(以及未添加哪些功能)。

通过查看源代码和 Javadoc 可以回答更多的问题。在阅读代码时,请记住以下经验法则

  • 查找名为 *AutoConfiguration 的类并阅读其来源。特别注意 @Conditional* 注释,以了解它们启用了哪些功能以及何时启用。将 --debug 添加到命令行或系统属性 -Ddebug,以获取应用程序中所做的所有自动配置决策的控制台日志。在启用了执行器的正在运行的应用程序中,查看 conditions 端点(/actuator/conditions 或 JMX 等效项)以获取相同的信息。

  • 查找 @ConfigurationProperties(例如 ServerProperties)的类,并从中读取可用的外部配置选项。@ConfigurationProperties 注释有一个 name 属性,该属性充当外部属性的前缀。因此,ServerProperties 具有 prefix="server",其配置属性为 server.portserver.address 等。在启用了执行器的正在运行的应用程序中,查看 configprops 端点。

  • 查找在 Binder 上使用 bind 方法以轻松的方式从 Environment 中明确提取配置值。它通常与前缀一起使用。

  • 查找直接绑定到 Environment@Value 注释。

  • 查找 @ConditionalOnExpression 注释,该注释根据 SpEL 表达式开关功能,通常使用从 Environment 解析的占位符进行评估。

在启动之前自定义 Environment 或 ApplicationContext

SpringApplication 具有 ApplicationListenersApplicationContextInitializers,用于对上下文或环境应用自定义。Spring Boot 从 META-INF/spring.factories 加载了许多此类自定义供内部使用。有多种方法可以注册其他自定义

  • 以编程方式,每个应用程序,通过在运行 SpringApplication 之前调用其 addListenersaddInitializers 方法。

  • 对于所有应用程序,通过添加 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 层次结构(添加父上下文或根上下文)

可以使用 ApplicationBuilder 类创建父/子 ApplicationContext 层次结构。有关详细信息,请参阅“Spring Boot 功能”部分中的“Fluent Builder API”。

创建非 Web 应用程序

并非所有 Spring 应用程序都必须是 Web 应用程序(或 Web 服务)。如果你想在 main 方法中执行一些代码,但同时启动 Spring 应用程序来设置要使用的基础设施,则可以使用 Spring Boot 的 SpringApplication 功能。SpringApplication 会根据它是否认为需要 Web 应用程序来更改其 ApplicationContext 类。你可以采取的第一个措施是将与服务器相关的依赖项(如 servlet API)从类路径中移除。如果你无法这样做(例如,你从相同的代码库运行两个应用程序),则可以在 SpringApplication 实例上显式调用 setWebApplicationType(WebApplicationType.NONE) 或设置 applicationContextClass 属性(通过 Java API 或外部属性)。你想作为业务逻辑运行的应用程序代码可以实现为 CommandLineRunner,并作为 @Bean 定义放入上下文中。