类文件
@Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
)
public @interface InEnum {
/**
* @return 实现 ArrayValuable 接口的类
*/
Class<? extends ArrayValuable<?>> value();
String message() default "必须在指定范围 {value}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
private List<?> values;
@Override
public void initialize(InEnum annotation) {
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
if (list == null) {
return true;
}
// 校验通过
if (CollUtil.containsAll(values, list)) {
return true;
}
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
return false;
}
}
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
private List<?> values;
@Override
public void initialize(InEnum annotation) {
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
// 为空时,默认不校验,即认为通过
if (value == null) {
return true;
}
// 校验通过
if (values.contains(value)) {
return true;
}
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
return false;
}
}
public interface ArrayValuable<T> {
/**
* @return 数组
*/
T[] array();
}
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements ArrayValuable<Integer> {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
/**
* 状态值
*/
private final Integer status;
/**
* 状态名
*/
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
public static boolean isEnable(Integer status) {
return ObjUtil.equal(ENABLE.status, status);
}
public static boolean isDisable(Integer status) {
return ObjUtil.equal(DISABLE.status, status);
}
}
// 元数据定义:CommonStatusEnum 提供状态数组
public enum CommonStatusEnum implements ArrayValuable<Integer> {
ENABLE(0, "开启"), DISABLE(1, "关闭");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(...);
@Override public Integer[] array() { return ARRAYS; }
}
// 元数据使用:验证器通过 ArrayValuable 获取校验范围
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
private List<?> values;
@Override public void initialize(InEnum annotation) {
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
this.values = Arrays.asList(values[0].array());
}
}
// 在 DTO 中使用自定义校验注解
public class UserStatusDTO {
@InEnum(CommonStatusEnum.class)
private Integer status; // 自动校验 status 是否为 0 或 1
}
特性 | 实现方式 |
---|---|
扩展性 | 新增枚举只需实现 ArrayValuable,无需修改验证逻辑 |
类型安全 | 通过泛型 ArrayValuable 保证元数据类型与校验值类型一致 |
校验语义明确 | @InEnum(CommonStatusEnum.class) 直观表达业务约束 |
错误提示友好 | 通过 context.buildConstraintViolationWithTemplate 动态生成错误信息 |
传统硬编码校验方式:
// 非模式化的校验逻辑(缺乏扩展性)
if (!(status == 0 || status == 1)) {
throw new IllegalArgumentException("状态值无效");
}