写点什么

自定义注解判断参数为空

作者:派大星
  • 2022-10-17
    辽宁
  • 本文字数:3137 字

    阅读完需:约 1 分钟

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


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 一完事,我忙完自己手里的活,果断的偷偷的把这个低级错误给办了。利用了自定义注解的方式。废话不多说给大家看一下核心代码。


  • 首先我建立了一个注解类


package com.mb.rks.cases.common.annotation;
import java.lang.annotation.*;
/** * @author * <p>自定义注解校验参数<p> */@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface MetadataValidation {
/** * 错误信息 * @return */ String message() default "参数不能为空";
/** * 正则表达式 * @return */ String pattern() default "";
}
复制代码


  • 然后利用反射写了一个验证器


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的参数进行校验 */@Slf4jpublic 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()); } } }

}
复制代码


  • 在需要调用校验参数的地方可以加上这样一串代码


// 点击发送之前的参数校验ValidationCheckUtils.check(dto);
复制代码


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


整理不易,欢迎微信搜索【码上遇见你】获取更多精彩内容。


发布于: 刚刚阅读数: 3
用户头像

派大星

关注

微信搜索【码上遇见你】,获取更多精彩内容 2021-12-13 加入

微信搜索【码上遇见你】,获取更多精彩内容

评论

发布
暂无评论
自定义注解判断参数为空_派大星_InfoQ写作社区