如何将配置文件变量传递给Spring Boot类注解?
能否将配置文件中的变量传递给类注解?
答案是不能直接通过${variable.from.property:31}这种占位符给注解的原始类型参数赋值,但可以通过修改注解参数类型并在校验器中解析配置的方式实现需求。
原因
Java注解的基本类型(如long、int)参数要求是编译时常量,而Spring的配置占位符${...}是运行时才会解析的动态值,编译阶段无法确定其具体数值,直接使用会触发编译错误。
解决方案:修改注解参数类型为String,在校验器中解析配置
1. 更新自定义注解MaxPeriod
将maxPeriod的类型从long改为String,这样就能接收占位符表达式:
@Target({ElementType.TYPE}) @Retention(RUNTIME) @Constraint(validatedBy = MaxPeriodValidator.class) @Repeatable(MaxPeriod.List.class) public @interface MaxPeriod { String message() default "Period between {firstDateField} and {secondDateField} must not exceed {maxPeriod} day(-s)"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String firstDateField(); String secondDateField(); // 改为String类型,支持配置占位符 String maxPeriod(); @Target({ElementType.TYPE}) @Retention(RUNTIME) @interface List { MaxPeriod[] value(); } }
2. 修改校验器MaxPeriodValidator,注入Environment解析占位符
让校验器实现EnvironmentAware接口,通过Spring的Environment来解析配置占位符,获取实际的最大周期值:
import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.lang.reflect.Field; import java.time.LocalDate; import java.time.temporal.ChronoUnit; public class MaxPeriodValidator implements ConstraintValidator<MaxPeriod, Object>, EnvironmentAware { private Environment environment; private String firstDateField; private String secondDateField; private String maxPeriodExpr; @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void initialize(MaxPeriod constraintAnnotation) { this.firstDateField = constraintAnnotation.firstDateField(); this.secondDateField = constraintAnnotation.secondDateField(); this.maxPeriodExpr = constraintAnnotation.maxPeriod(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { if (value == null) { return true; } try { // 解析配置占位符,获取实际最大天数 String resolvedMaxPeriod = environment.resolvePlaceholders(maxPeriodExpr); long maxPeriod = Long.parseLong(resolvedMaxPeriod); // 反射获取日期字段值 Field startFromField = value.getClass().getDeclaredField(firstDateField); startFromField.setAccessible(true); LocalDate startDateFrom = (LocalDate) startFromField.get(value); Field startToField = value.getClass().getDeclaredField(secondDateField); startToField.setAccessible(true); LocalDate startDateTo = (LocalDate) startToField.get(value); // 空值不校验(如需校验非空,可给字段加@NotNull注解) if (startDateFrom == null || startDateTo == null) { return true; } long actualPeriod = ChronoUnit.DAYS.between(startDateFrom, startDateTo); return actualPeriod <= maxPeriod; } catch (Exception e) { throw new RuntimeException("Max period validation failed", e); } } }
3. 使用方式不变
依然可以在类上使用占位符表达式:
@MaxPeriod(firstDateField = "startDateFrom", secondDateField = "startDateTo", maxPeriod = "${variable.from.property:31}") public class ProcessReportFilter { private LocalDate startDateFrom; private LocalDate startDateTo; }
补充说明
如果你的项目使用Spring Boot 2.2及以上版本,也可以尝试结合SpEL表达式(如#{@environment.getProperty('variable.from.property', '31')}),但本质上还是需要在校验器中解析表达式,上述String参数+Environment解析的方案更通用且易于维护。
内容的提问来源于stack exchange,提问作者ALEKSEY EMETS
相关产品推荐
相关产品推荐

