71. Spring Boot 应用程序

71.1 创建自己的 FailureAnalyzer

FailureAnalyzer是一种很好的拦截启动时异常并将其转换为人类可读的消息的方法,并包装在FailureAnalysis中。 Spring Boot 为应用程序上下文相关的异常,JSR-303 验证等提供了此类分析器。创建自己的实际上很容易。

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

FailureAnalyzer实现将在META-INF/spring.factories中注册:以下寄存器ProjectConstraintViolationFailureAnalyzer

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer

71.2 自动配置问题排查

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

任何 Spring Boot ApplicationContext中都有一个非常有用的ConditionEvaluationReport。如果启用DEBUG日志记录输出,则会看到它。如果您使用spring-boot-actuator,则还有一个autoconfig端点以 JSON 格式渲染报告。使用它来调试应用程序,并查看 Spring Boot 在运行时添加了哪些功能(哪些没有添加)。

通过查看源代码和 Javadoc,可以回答更多问题。一些经验法则:

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

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

  • 寻找使用RelaxedPropertyResolver将配置值明确地从Environment中拉出。它通常与前缀一起使用。

  • 查找直接绑定到Environment@ValueComments。这没有RelaxedPropertyResolver方法灵活,但确实允许一些轻松的绑定,特别是针对 OS 环境变量(因此CAPITALS_AND_UNDERSCORESperiod.separated的同义词)。

  • 寻找@ConditionalOnExpressionComments,这些 Comments 可根据 SpEL 表达式打开和关闭功能,这些 Comments 通常使用从Environment解析的占位符进行评估。

71.3 在启动 Environment 或 ApplicationContext 之前先对其进行自定义

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

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

  • 通过设置context.initializer.classescontext.listener.classes以声明方式针对每个应用程序。

  • 通过添加META-INF/spring.factories并将所有应用程序都用作库的 jar 文件打包,以声明方式针对所有应用程序。

SpringApplication向侦听器发送一些特殊的ApplicationEvents(甚至在创建上下文之前发送一些),然后还为ApplicationContext发布的事件注册侦听器。有关完整列表,请参见“ Spring Boot 功能”部分中的* 第 23.5 节“应用程序事件和侦听器” *。

还可以在使用EnvironmentPostProcessor刷新应用程序上下文之前自定义Environment。每个实现都应在META-INF/spring.factories中注册:

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

该实现可以加载任意文件并将其添加到Environment。例如,此示例从 Classpath 加载 YAML 配置文件:

public class EnvironmentPostProcessorExample 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) {
		if (!path.exists()) {
			throw new IllegalArgumentException("Resource " + path + " does not exist");
		}
		try {
			return this.loader.load("custom-resource", path, null);
		}
		catch (IOException ex) {
			throw new IllegalStateException(
					"Failed to load yaml configuration from " + path, ex);
		}
	}

}

Tip

Environment已经准备好了 Spring Boot 默认加载的所有常用属性源。因此,可以从环境中获取文件的位置。本示例将custom-resource属性源添加到列表的末尾,以便在其他任何常见位置定义的键都具有优先权。定制实现显然可以定义另一个 Sequences。

Note

虽然在@SpringBootApplication上使用@PropertySource似乎很方便,也很容易在Environment中加载自定义资源,但我们不建议您这样做,因为 Spring Boot 在刷新ApplicationContext之前会准备Environment。通过@PropertySource定义的任何键将加载得太晚,以至于对自动配置没有任何影响。

71.4 构建 ApplicationContext 层次结构(添加父上下文或根上下文)

您可以使用ApplicationBuilder类创建父/子ApplicationContext层次结构。有关更多信息,请参见“ Spring Boot 功能”部分中的* 第 23.4 节“ Fluent Builder API” *。

71.5 创建非 Web 应用程序

并非所有的 Spring 应用程序都必须是 Web 应用程序(或 Web 服务)。如果要用main方法执行一些代码,又要引导 Spring 应用程序来设置要使用的基础架构,那么使用 Spring Boot 的SpringApplication功能就很容易了。 SpringApplication根据是否认为需要 Web 应用程序来更改其ApplicationContext类。您可以做的第一件事就是让 servlet API 依赖项脱离 Classpath。如果您无法执行此操作(例如,您正在同一代码库中运行 2 个应用程序),则可以在SpringApplication实例上显式调用setWebEnvironment(false),或设置applicationContextClass属性(通过 Java API 或使用外部属性)。您可以将要作为业务逻辑运行的应用程序代码实现为CommandLineRunner,并可以将其作为@Bean定义放到上下文中。