定义枚举校验注解
/**
* 枚举校验注解
*
* @author wangzefeng
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValidator.class})
public @interface NotInEnum {
String message() default "{javax.validation.constraints.NotInEnum.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 校验的目标枚举类
*
* @return the Enum Class
*/
@NotNull Class<? extends Enum<?>> enumClass();
/**
* 使用校验的模式
*
* @return the EnumValidMode
* @see EnumValidMode
*/
@NotNull EnumValidMode mode() default EnumValidMode.NAME;
/**
* <p>
* 校验枚举的字段
* 当 {@link NotInEnum#mode()} = {@link EnumValidMode#FIELD} 时起作用
* 如果提供的字段不在目标枚举 {@link NotInEnum#enumClass()} 字段列表中,不校验
* 如果待校验字段值为 null, 不校验,需要校验 not null 请配合 {@link NotNull} 使用
* </p>
*
* @return fieldName
* @see NotNull
*/
String field() default "";
}
枚举校验模式
public enum EnumValidMode {
/**
* 枚举名称(即enum.name()/enum.toString())
*/
NAME,
/**
* 枚举序号(enum.ordinal())
*/
ORDINAL,
/**
* 枚举字段
*/
FIELD;
}
配置 I18N 错误信息文案
配置 I18N 错误信息文案需要在 classpath 下配置 ValidationMessages_{code}.properties,可参考hibernate validator 提供的 ValidationMessages.properties。
Maven 项目在 resources 新建 Resource Bundle,Resource Bundle base name 为 ValidationMessages,按需添加 zh_CN(中文/中国)、en(英文)等等方言。在 ValidationMessages_{code}.properties 中添加具体的文案信息即可。如:
javax.validation.constraints.NotInEnum.message=枚举不在范围内
枚举校验器
public class EnumValidator implements ConstraintValidator<NotInEnum, Object> {
private Class<? extends Enum<?>> enumClass;
private Enum<?>[] enumConstants;
private String fieldName;
private EnumValidMode enumValidMode;
@Override
public void initialize(NotInEnum constraintAnnotation) {
enumClass = constraintAnnotation.enumClass();
enumConstants = enumClass.getEnumConstants();
fieldName = constraintAnnotation.field();
enumValidMode = constraintAnnotation.mode();
}
@SneakyThrows
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
switch (enumValidMode) {
case NAME:
return useEnumName(value);
case ORDINAL:
return useOrdinal(value);
case FIELD:
return useField(value);
default:
return false;
}
}
/**
* 使用枚举的名称进行校验
*
* @param value the value
* @return true / false
*/
private boolean useEnumName(Object value) {
for (Enum<?> constants : enumConstants) {
if (constants.name().equals(value)) {
return true;
}
}
return false;
}
/**
* 使用枚举的序号进行校验
*
* @param value the value
* @return true / false
*/
private boolean useOrdinal(Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = enumClass.getMethod("ordinal");
for (Enum<?> constants : enumConstants) {
Object invoke = method.invoke(constants);
if (invoke.equals(value)) {
return true;
}
}
return false;
}
/**
* 使用枚举的字段进行校验
*
* @param value the value
* @return true / false
*/
private boolean useField(Object value) throws NoSuchFieldException, IllegalAccessException {
Field[] fields = enumClass.getDeclaredFields();
List<String> fieldNames = Stream.of(fields).map(Field::getName).collect(Collectors.toList());
if (!fieldNames.contains(fieldName)) {
return true;
}
Field field = enumClass.getDeclaredField(fieldName);
field.setAccessible(true);
for (Enum<?> constants : enumConstants) {
Object fieldValue = field.get(constants);
if (fieldValue.equals(value)) {
return true;
}
}
return false;
}
}