Skip to content

Spring Boot 应用

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

创建你的 FailureAnalyzer

FailureAnalyzer 是一种有效工具,可以拦截应用启动时的异常并将其转换为可读的消息,封装在 FailureAnalysis 中。Spring Boot 为与应用上下文相关的异常、JSR-303 验证等提供了内置的分析器。同时,你也可以创建自己的 FailureAnalyzer

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 属性用作外部属性的前缀。例如, ServerPropertiesprefix="server" ,因此其配置属性包括 server.portserver.address 等。如果运行的应用程序启用了 Actuator,可以查看 configprops 端点。
  • 查找使用 Binderbind 方法的地方,这种方法以宽松的方式从 Environment 中显式提取配置值。它通常与前缀一起使用
  • 查找使用 @Value 注解直接绑定到 Environment 的地方。
  • 查找使用 @ConditionalOnExpression 注解的地方,它们会根据 SpEL(Spring Expression Language) 表达式开启或关闭功能,这些表达式通常通过从 Environment 中解析的占位符来计算

在启动前自定义环境或者 ApplicationContext

SpringApplication 拥有 ApplicationListenersApplicationContextInitializers ,用于对上下文或环境进行自定义。Spring Boot 会从 META-INF/spring.factories 加载一些内置的自定义组件。这些组件可以通过以下方式注册:

  • 编程方式, 在运行应用之前,通过调用 SpringApplicationaddListenersaddInitializers 方法,为单个应用程序添加自定义组件。
  • 声明方式, 为所有应用添加自定义组件,在 META-INF/spring.factories 文件中注册相关类,并将其打包成 jar 文件,作为所有应用的依赖

SpringApplication 会向监听器发送一些特殊的 ApplicationEvents (有些事件在上下文创建之前就触发)。随后,它还会为由 ApplicationContext 发布的事件注册监听器。有关完整事件列表,请参阅 Spring Boot Features 部分的 Application Events and Listeners

可以通过实现 EnvironmentPostProcessor 接口,在应用上下文刷新之前自定义 Environment 。每个实现需要在 META-INF/spring.factories 文件中注册,例如

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

实现可以加载任意文件并将其添加到 Environment 中。例如,以下示例从类路径中加载一个 YAML 配置文件

java
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);
		}
	}

}

Environment 已经通过 Spring Boot 默认加载的所有常见属性源进行了初始化。因此,可以从 Environment 中获取文件的位置。前述示例将 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) ,或者通过 Java API 或外部属性设置 applicationContextClass 属性。

作为业务逻辑的应用代码可以实现为一个 CommandLineRunner ,并以 @Bean 定义的形式放入上下文中运行。