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 在运行时添加了哪些功能(以及哪些功能未添加)。

查看源代码和 API 文档可以解答更多问题。阅读代码时,请记住以下经验法则:

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

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

  • 查找Binder上的bind方法的使用,以便以宽松的方式显式地从Environment中提取配置值。它通常与前缀一起使用。

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

  • 查找@ConditionalOnExpression注解,这些注解根据 SpEL 表达式(通常使用从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会更改其ApplicationContext类,具体取决于它是否认为需要 Web 应用程序。您可以做的第一件事是帮助它从类路径中删除服务器相关的依赖项(例如 servlet API)。如果您无法做到这一点(例如,如果您从同一个代码库运行两个应用程序),则可以在您的SpringApplication实例上显式调用setWebApplicationType(WebApplicationType.NONE),或设置applicationContextClass属性(通过 Java API 或外部属性)。您要作为业务逻辑运行的应用程序代码可以实现为CommandLineRunner,并作为@Bean定义放入上下文。