【问题标题】:How to conditionally make a Spring Boot application terminate at startup based on a property如何根据属性有条件地使 Spring Boot 应用程序在启动时终止
【发布时间】:2019-10-02 18:35:42
【问题描述】:

我想实现以下用例 - 我的 Spring Boot 应用程序只有在设置了 application.yaml 中的某个属性时才应该启动:

myapp:
  active: true

如果未设置该属性,则上下文初始化应失败并显示该属性缺失的消息。

我在这个主题中找到了如何实现它:Spring Boot - Detect and terminate if property not set? 但是我不能遵循这种方法的问题是,在检查这个属性的 bean 加载之前上下文初始化可能会失败。

例如,如果某个其他 bean 因缺少另一个属性而无法加载,那么上下文初始化将在此时失败,并且检查所需属性的我的 bean 将不会被加载。这对我来说不行,因为我希望在加载任何其他 bean 之前先检查 myapp.active 属性。

我想要这样的原因是在运行应用程序时应该设置某个配置文件 - application-[profile].yaml 包含 myapp.active: true 和加载上下文所需的其他一些强制属性。

我希望我的应用程序总是因为 myapp.active 不正确而失败,以便我可以输出一条有意义的消息,告知配置文件丢失并且该应用程序必须使用其中一个配置文件运行(来自给定的配置文件列表)。一些不是开发人员的人正在运行该应用程序,所以我希望他们知道为什么该应用程序没有运行,否则他们会认为该应用程序中存在一些错误。

我怎样才能做到这一点?是否有可能在加载 bean 之前以某种方式读取属性?我想避免在所有 bean 上设置 @DependsOn(或通过 BeanPostProcesser 做同样的事情)并寻求更优雅的解决方案。

【问题讨论】:

  • 如果你已经在某个字段中提供了@value注解,并且该字段在属性中不存在,spring boot会自动终止。
  • 同意,但这还不够。仅当包含 @Value 注释的 bean 恰好是在初始化期间失败的第一个(或唯一一个)bean 时,这才会失败。
  • 那么剩下的唯一选择就是在 Spring 中使用 PostProcessor 的自定义 bean 实例化逻辑。您可以使用 Spring Bean PostProcessor。

标签: java spring spring-boot


【解决方案1】:

如果您使用属性条件,应用程序将不会启动。够快吗?

 @SpringBootApplication
 @ConditionalOnProperty(name = "myapp.active")
 public class FastFailWhenPropertyNotPresentApplication {

     public static void main(String[] args) {
            SpringApplication.run(FastFailWhenPropertyNotPresentApplication.class, args);
    }

}

基本上@SpringBootApplication 只是一个@Configuration 类。

您有一个选项matchIfMissing,可用于指定如果未设置属性,条件是否应匹配。默认为 false。

编辑:

更好的解决方案是通过@ConfigurationProperties 结合@Validated 来配置您的属性,这样您就可以使用 javax.validation.constraints 注释。

package stackoverflow.demo;

import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@Component
@ConfigurationProperties(prefix = "myapp")
@Validated
public class MyAppProperties {

  @AssertTrue
  @NotNull
  private Boolean active;

  public Boolean getActive() {
    return active;
  }

  public void setActive(Boolean active) {
    this.active = active;
  }

}

注意:你可以省略@ConditionalOnProperty(name = "myapp.active")

@AssertTrue@NotNull 结合使用,因为@AssertTrue认为空元素是有效的。

spring-boot 会免费生成一个不错的错误消息:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myapp' to stackoverflow.demo.MyAppProperties failed:

    Property: myapp.active
    Value: false
    Origin: class path resource [application.properties]:1:16
    Reason: must be true


Action:

Update your application's configuration

编辑(更新问题后)


更快的方法:您的应用程序不会启动,也不会加载应用程序上下文

package stackoverflow.demo;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;

@SpringBootApplication
public class FastFailWhenPropertyNotPresentApplication {

  static Boolean active;

  static {

    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("application.yaml"));

    active = (Boolean) yaml.getObject().getOrDefault("myapp.active", false);

  }


    public static void main(String[] args) {
        if (!active) {
          System.err.println("your fail message");
        } else {
          SpringApplication.run(FastFailWhenPropertyNotPresentApplication.class, args);
        }
    }

}

编辑

另一种可能最适合您需求的解决方案...

通过收听ApplicationEnvironmentPreparedEvent

在 {@link SpringApplication} 启动时发布的事件并且 * {@link Environment} 首先可用于检查和修改。 *

注意:您不能使用@EventListener,但您已将侦听器添加到SpringApplication

package stackoverflow.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;

@SpringBootApplication
public class FastFailWhenPropertyNotPresentApplication {


  static class EnvironmentPrepared implements ApplicationListener<ApplicationEnvironmentPreparedEvent>{
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
      Boolean active = event.getEnvironment().getProperty("myapp.active",Boolean.class,Boolean.FALSE);
      if(!active) {
        throw new RuntimeException("APPLICATION FAILED TO START: ACTIVE SHOULD BE TRUE ");
      }
    }
  };


  public static void main(String[] args) throws Exception {
    SpringApplication springApplication = new SpringApplication(FastFailWhenPropertyNotPresentApplication.class);
    springApplication.addListeners(new FastFailWhenPropertyNotPresentApplication.EnvironmentPrepared());
    springApplication.run(args);
  }

}

【讨论】:

  • 我已经知道这种方法,但它不能满足我的需求,因为我想在这种情况下输出给定的消息,正如我在问题开头所述:“如果未设置属性,上下文初始化应该失败,并显示一些属性丢失的给定消息。"
  • 如果上下文由于其他原因而无法加载怎么办,例如,如果缺少某些 bean 依赖项?另一个错误仍然有可能首先发生。我希望应用程序在启动时始终失败,因为 myapp.active 为假(或丢失),我希望它比启动期间可能发生的任何不良事件具有更高的优先级。
  • 我更新了问题以提供更多上下文 :-)
  • 直接从 application.yaml 文件加载属性对我来说不是一个选项,我无法硬编码路径,因为它也可以在配置文件特定的 yaml 文件之一中设置(application-.yaml),正如我所写....无法预测它将在哪个特定的 app.yaml 文件中设置...如果它在 application.yaml 或任何特定于配置文件的 application-.yaml 中设置为 true,那么应用程序应该启动,否则不启动,这是唯一的条件......
【解决方案2】:

一种选择是创建一个 bean 来检查属性是否存在,如果没有设置属性,则在 bean 创建期间抛出异常。

@Component
public static EnsureApplicationActive {
    @Value("${myapp.active}")
    private Boolean active;

    @PostConstruct
    public void ensureApplicationActive() {
        if (!Boolean.TRUE.equals(active)) {
            throw new IllegalStateException("TODO: Meaningful message goes here");
        }
    }
}

【讨论】:

    猜你喜欢
    • 2017-03-05
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-22
    相关资源
    最近更新 更多