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*注解,以了解它们何时以及启用了哪些功能。在命令行或系统属性-Ddebug中添加--debug,以在控制台上获取您的应用程序中所有自动配置决策的日志。在启用 Actuator 的运行中的应用程序中,查看conditions端点(/actuator/conditions或其 JMX 等效项)以获取相同信息。 -
查找带有
@ConfigurationProperties注解的类(例如ServerProperties),并从中读取可用的外部配置选项。@ConfigurationProperties注解有一个name属性,用作外部属性的前缀。因此,ServerProperties的prefix="server",其配置属性是server.port、server.address等。在启用 Actuator 的运行中的应用程序中,查看configprops端点。 -
查找
Binder上的bind方法的使用,以宽松的方式从Environment中显式提取配置值。它通常与前缀一起使用。 -
查找直接绑定到
Environment的@Value注解。 -
查找
@ConditionalOnExpression注解,它们响应 SpEL 表达式来开启或关闭功能,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 中。例如,以下示例从 classpath 中加载 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 特性”部分中的 Fluent Builder API。
创建非 Web 应用程序
并非所有 Spring 应用程序都必须是 Web 应用程序(或 Web 服务)。如果您想在 main 方法中执行一些代码,同时也引导一个 Spring 应用程序来设置基础设施,您可以使用 Spring Boot 的 SpringApplication 特性。SpringApplication 会根据它是否认为需要 Web 应用程序来改变其 ApplicationContext 类。您可以做的第一件事就是将与服务器相关的依赖项(例如 servlet API)排除在 classpath 之外。如果不能这样做(例如,如果您从同一个代码库运行两个应用程序),那么您可以显式地在您的 SpringApplication 实例上调用 setWebApplicationType(WebApplicationType.NONE),或者设置 applicationContextClass 属性(通过 Java API 或外部属性)。您希望作为业务逻辑运行的应用程序代码可以实现为 CommandLineRunner,并作为 @Bean 定义放入上下文中。