Spring在没有setter的情况下验证@Value

Chi*_*hak 3 java validation spring json

我的配置文件中有一些值,它应该是 JSON(它将作为字符串加载)。

我希望 Spring 在注入之前验证该值确实是有效的 JSON,否则会抛出错误。

我按如下方式注入它:

@Value("${source.SomeJsonString}")
private String someJsonString;
Run Code Online (Sandbox Code Playgroud)

我看到了以下内容:How to make simple propertyvalidation when using Spring @Value

但是,由于我有多个应该注入的类source.SomeJsonString,所以我不想为每个类创建一个setter,并一次又一次地编写验证。

有没有办法只写一次验证器?

我考虑过创建注释(Spring validate string value is a JSON),但似乎注释的值@Value无法验证。

还有其他办法吗?

Evg*_*yst 5

Spring 外部化配置可以使用 JSR 303 Bean Validation API 进行验证。但它需要Spring 类型安全的配置属性而不是@Value("${property}").

将 Hibernate Validator 依赖项添加到build.gradle

implementation 'org.hibernate.validator:hibernate-validator'
Run Code Online (Sandbox Code Playgroud)

类型安全配置属性必须使用自定义注释进行@Validated注释someJsonString@ValidJsonConstraint

@Component
@ConfigurationProperties("source")
@Validated
public class SourceProperties {

  @ValidJsonConstraint
  private String someJsonString;

  public String getSomeJsonString() {
    return someJsonString;
  }

  public void setSomeJsonString(String someJsonString) {
    this.someJsonString = someJsonString;
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以将属性注入到所有必需的服务中,因此验证代码不会重复

@Autowired
private SourceProperties sourceProperties;
Run Code Online (Sandbox Code Playgroud)

是时候创建自定义注释了

@Documented
@Constraint(validatedBy = ValidJsonValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidJsonConstraint {

  String message() default "Invalid JSON";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}
Run Code Online (Sandbox Code Playgroud)

以及一个验证器,用于验证使用自定义注释注释的字段

public class ValidJsonValidator implements ConstraintValidator<ValidJsonConstraint, String> {

  private final ObjectMapper objectMapper = new ObjectMapper();

  @Override
  public void initialize(ValidJsonConstraint constraintAnnotation) {
  }

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    try {
      objectMapper.readTree(value);
      return true;
    } catch (JsonProcessingException e) {
      return false;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

当application.properties中的值source.someJsonString是有效的 JSON时

source.someJsonString={"test":"qwe"}
Run Code Online (Sandbox Code Playgroud)

应用程序成功启动。

当 JSON 无效时

source.someJsonString=qwe
Run Code Online (Sandbox Code Playgroud)

应用程序无法启动,出现以下异常

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

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'source' to intellectsoft.afgruppen.shiftschedule.SourceProperties failed:

    Property: source.someJsonString
    Value: qwe
    Origin: class path resource [application.properties]:26:23
    Reason: Invalid JSON


Action:

Update your application's configuration
Run Code Online (Sandbox Code Playgroud)

另外,如果没有 JSR 303 Bean Validation API,也可以更容易地实现同样的效果。

创建自定义验证组件

@Component
public class JsonValidator {

  private final ObjectMapper objectMapper = new ObjectMapper();

  public boolean isValid(String value) {
    try {
      objectMapper.readTree(value);
      return true;
    } catch (JsonProcessingException e) {
      return false;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

注入验证器并在属性设置器中执行验证

@Component
@ConfigurationProperties("source")
public class SourceProperties {

  private final JsonValidator jsonValidator;

  private String someJsonString;

  public SourceProperties(JsonValidator jsonValidator) {
    this.jsonValidator = jsonValidator;
  }

  public String getSomeJsonString() {
    return someJsonString;
  }

  public void setSomeJsonString(String someJsonString) {
    if (!jsonValidator.isValid(someJsonString)) {
      throw new IllegalArgumentException(someJsonString + " is not a valid JSON");
    }
    this.someJsonString = someJsonString;
  }
}
Run Code Online (Sandbox Code Playgroud)