自定义注解@UniqueProperty
UniqueProperty 注解允许你在集合中的元素上指定某个属性,并使用 UniquePropertyValidator 类来确保该属性的值在集合中是唯一的。你可以通过注解的属性来自定义校验的行为,包括校验失败时的错误消息、属性是否可以为 null 等。
代码如下:
-
@Target(FIELD):这个注解告诉编译器,UniqueProperty注解应该只能用在类的字段(属性)上。 -
@Retention(RUNTIME):这个注解指定了UniqueProperty的保留策略,即该注解会保留在编译后的类文件中,并在运行时可以通过反射来访问。 -
@Documented:这个注解用于指示注解将包含在 Javadoc 文档中。 -
@Constraint(validatedBy = UniquePropertyValidator.class):这是一个约束注解,它告诉验证器要使用UniquePropertyValidator类来执行实际的校验逻辑。
/**
* 用于集合中每一个元素的某个相同属性进行值的唯一性校验
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = UniquePropertyValidator.class)
public @interface UniqueProperty {
/**
* 默认0为类中的第一个属性,若类中出现序列版本号,则0指向序列版本号。
* @return
*/
int index() default 0;
String message() default "invalid value";
boolean canNull() default false;
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
验证器的主要功能包括:
-
通过
initialize方法来获取UniqueProperty注解上的配置信息,包括要校验的属性位置(index)和是否允许属性为 null(canNull)。 -
通过
isValid方法来执行实际的校验逻辑。它首先检查集合是否为空,如果为空并且允许属性为 null,则认为校验通过。如果为空但不允许属性为 null,则认为校验失败。接下来,它遍历集合中的元素,获取指定位置的属性值,并将这些属性值添加到一个集合中进行唯一性校验。如果发现重复的属性值,或者某个属性值为 null(如果不允许属性为 null),则认为校验失败。 -
getFieldValue方法用于通过反射获取对象中指定位置属性的值。它首先获取对象的所有字段,然后根据指定的索引位置获取字段名称,进而获取属性值。这个方法的目的是根据index属性指定的位置获取属性值。
@Slf4j
public class UniquePropertyValidator implements ConstraintValidator<UniqueProperty ,Collection<?>> {
private int index;
private boolean canNull;
@Override
public void initialize(UniqueProperty constraintAnnotation) {
index = constraintAnnotation.index();
canNull = constraintAnnotation.canNull();
}
@Override
public boolean isValid(Collection<?> objects, ConstraintValidatorContext context) {
if(CollectionUtil.isEmpty(objects)){
if(canNull) {
return true;
} else {
return false;
}
}
Set<Object> uniqueValues = new HashSet<>();
for (Object obj : objects) {
//获取指定属性值
Object param = getFieldValue(obj, index);
if (param == null || uniqueValues.contains(param)) {
return false;
}
uniqueValues.add(param);
}
return true;
}
public static Object getFieldValue(Object object, int index) {
// 使用 Java 反射获取对象的所有字段
Field[] fields = object.getClass().getDeclaredFields();
if (fields.length == 0) {
throw new IllegalArgumentException("Object has no fields.");
}
try {
String fieldName = fields[index].getName();
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object fieldValue = field.get(object);
log.warn("属性值唯一性校验:{}的值为{}", fieldName, fieldValue);
return fieldValue;
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
示例:
@UniqueProperty(message = "XX出现重复!", index = 1)
private List<User> users;
注意:1、通过若对象中存在SerialVersionUID字段,那么index = 0会指向SerialVersionUID!
2、要在Controller加上@Valid注解!
3、多层对象嵌套也需要逐层添加@Valid注解!

浙公网安备 33010602011771号