自定义注解判断参数为空

2023-06-28 14:58:52 浏览数 (1)

最近在项目中遇到了一个小小的问题,和大家分享一下,简单的接口但是在不同的业务场景下需要有不同的校验逻辑,有的参数在特定的场景下需要校验,有的参数在另外的场景下则不需要校验。解决方案有很多种加上我当时是刚刚入职为了偷懒贪图省事,所以就写了一大堆的if/else。如下展示(由于业务原因,敏感字段已转换):

代码语言:javascript复制
public void checkParams(DTO dto) {
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }
    if (ObjectUtils.isEmpty(dto.getParam())) {
        throw new ServiceException("Please fill in the param number");
    }

    ..............
}

为了方法的可读性吧,我还特意将检验参数的方法抽离出checkParams单独的方法。当时想这样应该也就可以了吧。万万没想到啊,我在编写完代码的时候在pull代码前的编译的时候,PMD检查没有过这个是把我给恶心到了当时报了一个错叫做什么 Avoid really long methods 和 GodClass这就让我很尴尬了,临门一脚给我拦住了很是郁闷。当时想我一个新来的需要抓紧把代码提上去不能拖延时间啊,接着我又开始了一系列的骚操作是你们不可能想到的。如下:

相比各位童鞋们看到参数的命名很无语吧,当时我也很无语其实我下意识是不想这么做的。但是没有办法,当时着急熟悉代码。以及完成工作没办法我还是硬着头皮把这个代码给改掉了。想着以后有时间改掉吧。天不遂人愿啊真的是。隔天我就经历了code review,各位你们可知道那种场合,我写的代码被斩首示众,我当时简直脚趾抠出来三室一厅的尴尬,好在是我leader明白我当时的困境理解我(此时我还是很庆幸的),说没事有时间改过来就好。cw一完事,我忙完自己手里的活,果断的偷偷的把这个低级错误给办了。利用了自定义注解的方式。废话不多说给大家看一下核心代码。

  • 首先我建立了一个注解类
代码语言:javascript复制
package com.mb.rks.cases.common.annotation;

import java.lang.annotation.*;

/**
 * @author
 * <p>自定义注解校验参数<p>
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MetadataValidation {

    /**
     * 错误信息
     * @return
     */
    String message() default "参数不能为空";

    /**
     * 正则表达式
     * @return
     */
    String pattern() default "";

}

  • 然后利用反射写了一个验证器
代码语言:javascript复制
package com.mb.rks.cases.common.utils;

import com.mb.rks.cases.common.annotation.MetadataValidation;
import com.mb.rks.cases.common.exception.TipsValidationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author
 * <p>Validation验证器<p>
 * <p>
 * 对有自定义注解@Validation的参数进行校验
 */
@Slf4j
public final class ValidationCheckUtils {

    /**
     * 因为是工具类,所以不可以通过new的方式去创建,顾将其构造方法写成如下方式
  */
    protected ValidationCheckUtils(){}

    /**
     * 检验方法:
     * <p>扫描对象的属性,查看是否有@Validation注解,有注解的进行校验
     *
     * @param o 要校验的对象,入参对象
     */
    public static void check(Object o) {
        if (null == o) {
            return;
        }
        Class<?> clazz = o.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }

        // 对象的所有属性
        fieldList.forEach(field -> {
            ReflectionUtils.makeAccessible(field);

            try {
                // 获取属性值
                MetadataValidation annotation = field.getAnnotation(MetadataValidation.class);
                // 没有注解则不做处理
                if (null == annotation) {
                    return;
                }
                // 获取指定对象o上此 Field 表示的字段的值
                Object value = field.get(o);
                checkNotNull(value, annotation);
                checkPattern(value, annotation);
            } catch (IllegalAccessException e) {
                log.error("Validation验证起数据解析失败:{}", e.getMessage());
            }
        });
    }


    /**
     * 非空判断
     *
     * @param value      属性值
     * @param annotation 注解信息
     */
    private static void checkNotNull(Object value, MetadataValidation annotation) {
        log.info("开始非空校验");
        if (annotation != null && ObjectUtils.isEmpty(value)) {
            throw new RuntimeException(annotation.message());
        }
    }

    /**
     * 正则校验
     *
     * @param value      属性值
     * @param annotation 注解信息
     */
    private static void checkPattern(Object value, MetadataValidation annotation) {
        // 存在正则表达式
        if (null != annotation.pattern() && annotation.pattern().length() > 0) {
            Pattern pattern = Pattern.compile(annotation.pattern());
            // 以pattern的规则匹配value的值
            Matcher matcher = pattern.matcher(value.toString());
            // 属性值不符合正则表达式所制定的格式,抛出异常
            if (!matcher.matches()) {
                throw new RuntimeException(annotation.message());
            }
        }
    }


}

  • 在需要调用校验参数的地方可以加上这样一串代码
代码语言:javascript复制
// 点击发送之前的参数校验
ValidationCheckUtils.check(dto);

好了上面所有的核心代码快已经贴出来了,希望能够帮助到小伙伴们。 其实看到这里的小伙伴们免不了会说上一句使用Spring的 @Valid和@Validated不好嘛,干嘛要自己造轮子呢,多次一举,其实不是这样的这种想法我在编写代码的时候就想到了。但是在我们的业务场景中多个参数接口使用的参数类是同一个,所以使用Spring的@Valid和@Validated自然是不行了。其实换种想法也不是不可以那就是检验参数的再新建一个类,我觉得完全没必要,因为这样很容易就造成类爆炸。不知你是怎么样呢。欢迎评论。

0 人点赞