Spring validator-注解验证-自定义注解
1.@NotNull,@NotBlank等常用基础原生注解
本篇文章主讲:高级用法:自定义注解.
需要了解使用基础原生注解,请参考:
链接:
Spring validator常用注解验证.
https://blog.csdn.net/weixin_44903702/article/details/110953515.
2.自定义注解.
目标:
实现默认原生注解中没有的效验方式-
比如: 本文实现的;
本文实现效果如下:
- 效验参数必须是指定的值;
- 指定的值,即可以指定我们代码中的定义的枚举类的值;
- 指定的值,也可以我们手动指定固定的值;
- 接收string和Integer类型的效验,其他类型,自定义即可;
1.自定义注解 – 类:
package com.hzsmk.common.base.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义参数效验-必须为指定值
*
* @author weixz
* @date 2020-12-10 10:29
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValueValidator.class})
public @interface ValidValue {
//默认错误消息
String message() default "参数有误!必须为指定值!";
String[] strValues() default {};
int[] intValues() default {};
Class<? extends Enum> enumValue() default Enum.class;
//注意此字段值,是枚举效验时,在枚举类里实现的方法名-"checkParam"只是默认方法名,可手动设置其他参数覆盖此值,(比如同一个枚举类里同时设定了多值,可以有多套效验规则时)
String methodName() default "checkParam";
//分组
Class<?>[] groups() default {};
//负载
Class<? extends Payload>[] payload() default {};
//指定多个时使用
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
ValidValue[] value();
}
}
2.自定义注解 – 效验器:
package com.hzsmk.common.base.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;
/**
* 自定义参数效验-必须为指定值-效验器
*
* @author weixz
* @date 2020-12-10 10:33
*/
public class EnumValueValidator implements ConstraintValidator<ValidValue, Object> {
private String[] strValues;
private int[] intValues;
private Class<? extends Enum> enumClass;
private String methodName;
@Override
public void initialize(ValidValue constraintAnnotation) {
enumClass = constraintAnnotation.enumValue();
if (enumClass != Enum.class){
methodName = constraintAnnotation.methodName();
try {
// 先判断该enum是否实现了toEnum方法
enumClass.getDeclaredMethod(methodName, Object.class);
} catch (NoSuchMethodException e){
throw new IllegalArgumentException("未添加效验规则,请联系管理员!", e);
}
}
strValues = constraintAnnotation.strValues();
intValues = constraintAnnotation.intValues();
}
/**
* 优先效验枚举参数,若无枚举参数时,再效验手动指定值
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(Object value, ConstraintValidatorContext context ) {
if (enumClass != Enum.class){
Method declareMethod;
try {
declareMethod = enumClass.getDeclaredMethod(methodName, Object.class);
}catch (NoSuchMethodException e){
return false;
}
try {
declareMethod.invoke(null, value);
} catch (Exception e) {
return false;
}
return true;
}else {
if(value instanceof String) {
for (String s:strValues) {
if(s.equals(value)){
return true;
}
}
}else if(value instanceof Integer){
for (Integer s:intValues) {
if(s==value){
return true;
}
}
}
return false;
}
}
}
3.枚举-实现方法:
注意:checkParam方法
package com.hzsmk.lstalent.consts;
import com.hzsmk.common.exception.BusinessException;
import java.util.ArrayList;
import java.util.List;
/**
* 测试枚举
*
* @author weixz
* @date 2020-12-10 10:40
*/
public enum TestTypeEnum {
TEST1("test1", "类型1"),
TEST2("test2", "类型2"),
TEST3("test3", "类型3");
//补贴名称
private String name;
//补贴类型
private String type;
TestTypeEnum(String name, String type) {
this.name = name;
this.type = type;
}
public static List<String> getNames() {
ArrayList<String> names = new ArrayList<>();
for (TestTypeEnum typeEnum : TestTypeEnum.values()) {
names.add(typeEnum.getName());
}
return names;
}
public static List<String> getTypes() {
ArrayList<String> types = new ArrayList<>();
for (TestTypeEnum typeEnum : TestTypeEnum.values()) {
types.add(typeEnum.getType());
}
return types;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// **********************注意此方法,********重点
// 注解校验器的默认方法
public static void checkParam(Object name) {
List<String> names = TestTypeEnum.getNames();
//参数不在该枚举name的范围内,返回
if (!names.contains(String.valueOf(name))){
throw new BusinessException("参数不在指定范围内");
}
}
//自定义的其他效验方式方法
public static void checkType(Object type) {
List<String> types = TestTypeEnum.getTypes();
//参数不在该枚举的type范围内
if (!types.contains(String.valueOf(type))){
throw new BusinessException("参数不在指定范围内");
}
}
}
3.使用及代码详解;
1.使用:
@Data
public class TestDto implements Serializable {
@ValidValue(enumValue = TestTypeEnum.class)
private String test1;
@ValidValue(strValues = {"张三","李四","王五"})
private String test2;
@ValidValue(intValues = {1,5,7})
private String test3;
@ValidValue(enumValue = TestTypeEnum.class , methodName = "checkType")
private String test4;
}
2.代码解析(不太清晰的看这里,或许有帮助):
1.自定义的ValidValue注解类中
我们看的到:在自定义的ValidValue注解类中:
共有7个属性:
//默认错误消息
String message() default "参数有误!必须为指定值!";
String[] strValues() default {};
int[] intValues() default {};
Class<? extends Enum> enumValue() default Enum.class;
//注意此字段值,是枚举效验时,在枚举类里实现的方法名-"checkParam"只是默认方法名,可手动设置其他参数覆盖此值,(比如同一个枚举类里同时设定了多值,可以有多套效验规则时)
String methodName() default "checkParam";
//分组
Class<?>[] groups() default {};
//负载
Class<? extends Payload>[] payload() default {};
其中:
我们自定义的有4个:
分别是:
- strValues : 允许的字符串值
- intValues : 允许的数字值
- enumValue: 枚举类的Class
- methodName: 枚举类中实现效验的方法名
2.自定义的效验器中
1.属性
private String[] strValues;
private int[] intValues;
private Class<? extends Enum> enumClass;
private String methodName;
是四个属性;后面会参与效验;
2.方法
@Override
public void initialize(ValidValue constraintAnnotation) {
XXXXXXXXXXXXXXXX
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context ) {
XXXXXXXXX
}
两个方法都是重新的父类方法:
其中:
initialize方法:
是初始化方法,其会加载我们设置的值进效验器中;
isValid方法:
是效验参数的规则逻辑,返回值true为通过,false为不通过;
至于上面提的,枚举类中,checkParam方法,就是在isValid效验方法中使用反射的方法调用的方法,如果没有实现的话,会抛出异常哦;
大家可以根据自己的需求和想法,对代码仅需修改改造,以便于更适用于自己;