实现注解校验Dto字段是否为空

一、背景

我们用json对象作为接收参数的包装器,最后要转化为dto进行业务操作,操作之前要做非空校验,我们可以实现2个注解来实现这个通用的操作。@NotNullField @CheckNull

二、思路

1.实现@NotNullField注解,注解标记在dto字段名上面

@Target(ElementType.FIELD) // 目标为字段
@Retention(RetentionPolicy.RUNTIME) // 运行时可用
public @interface NotNullField {
    String fieldName() default ""; // 可自定义字段名
    String message() default "字段不能为空"; // 默认错误消息
}

image
2.实现@CheckNull注解,注解标记在调用方法上面,这个是要接受dto.class的
image

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNull {
    //要校验的dto
    Class<?> value();
}

三、代码实现

1.@NotNullField,这个我实现了一个静态方法

 /**
     * 校验标注了非空字段的dto
     * @param obj
     * @throws NullParamsException
     */
     public static void validateNotNull(Object obj) throws NullParamsException {

        Class<?> clazz = obj.getClass();

        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(NotNullField.class)) {
                    field.setAccessible(true); // 允许访问私有字段
                    try {
                        Object value = field.get(obj);
                        NotNullField annotation = field.getAnnotation(NotNullField.class);
                        if (value == null) {
                            throw new NullParamsException(
                                    String.format("验证错误: %s - %s - %s", annotation.fieldName(),field.getName(),annotation.message())
                            );
                        }else{
                            /**
                             * 字符串字段不能为空
                             */
                            if(field.getType().equals(String.class)){
                                String s = (String) value;
                                if(StringUtil.isEmpty(s)) {
                                    throw new NullParamsException(
                                            String.format("验证错误: %s - %s - %s", annotation.fieldName(),field.getName(),annotation.message())
                                    );
                                }
                            }
                            /**
                             *如果是集合类型递归调用
                             *感觉递归调用 是一个危险的方式 大数据量会导致栈内存溢出 先进行实验
                             *代码中如果有集合的方式必须 业务自己提取出来data自己去遍历校验似乎更好
                             */
                            /**
                             * 集合不能为空
                             */
                            if (value instanceof Collection) {
                                Collection<?> collection = (Collection<?>) value;
                                if(collection.isEmpty()){
                                    throw new NullParamsException(
                                            String.format("验证错误: %s - %s - %s", annotation.fieldName(),field.getName(),annotation.message())
                                    );
                                }
                                Iterator<?> iterator = collection.iterator();
                                while (iterator.hasNext()) {
                                    Object item = iterator.next();
                                    // 处理集合中的每个对象
                                    validateNotNull(item);
                                }
                            }
                        }
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("访问错误", e);
                    }
                }
            }
            // 移动到父类 要支持继承
            clazz = clazz.getSuperclass();
        }
    }

2.@CheckNull,这里的@Order 注解是标记注解起作用的顺序,因为我还写了其他校验注解

@Component
@Aspect
@Order(2)
public class CheckNullAspect {
    @Autowired
    private MyAutoConfig myAutoConfig;
    /**
     * 环绕处理
     * 连接点
     * 注解的全限定名称
     * @return 结果
     * @throws Throwable 异常
     */
    @Pointcut(value = "@annotation(具体包)")
    public void checkAround() throws Throwable {

    }

    @Before("checkAround()")
    public void around(JoinPoint proceedingJoinPoint) throws Throwable {
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //获取操作
        CheckNull annotation = method.getAnnotation(CheckNull.class);
        Class<?> dtoClass = annotation.value();
        // 获取入参
        Object[] objects = proceedingJoinPoint.getArgs();

        if (Objects.nonNull(objects) && objects.length > 0) {
            JSONObject jsonObject = (JSONObject) objects[0];
            Object dto = jsonObject.toJavaObject(dtoClass);
            CheckUtil.validateNotNull(dto);
        }
    }
}

四、后记

自定义注解实现在spring类型的项目里面超好用!

posted @   lovefoolself  阅读(42)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示