如何优雅做参数校验?

1.日常如何做校验?

在日常开发中我们大多数人都是借助Pattern对象,然后直接给指定的表达式来创建他,当然还有人会使用别的方式来校验,我们来看下使用Pattern来如何校验?假设我们现在需要校验一个字符串是否为数字,那么我们校验的逻辑应该是这样:

    public  void businessMethod(String money){
        Pattern compile = Pattern.compile("\\d+");
        Matcher matcher = compile.matcher(money);
        // 检验金额是否是数字
        if (!matcher.matches()){
            throw  new RuntimeException("money 不合法!");
        }
        // 继续向下执行业务
    }

这样做会有什么影响?

我们先来看下 Pattern 的compile方法,可以看到 每次使用正则 compile 的时候都会创建一个 Pattern 对象,其实我们Pattern 的这个对象是被创建好了,下次我们可以直接使用的,但是我们并没保存起来,而是每做一次校验,就创建了一个 Pattern对象。假设我们现在有1千万的数据进来,需要做校验,那么我们就会创建1千万个Pattern对象,在这个对象被用完后又要被JVM的垃圾回收期回收掉,后续要校验又得重新创建,这样周而复始。会使用我们程序的可用性大打折扣。

    /**
     * Compiles the given regular expression into a pattern.
     *
     * @param  regex
     *         The expression to be compiled
     * @return the given regular expression compiled into a pattern
     * @throws  PatternSyntaxException
     *          If the expression's syntax is invalid
     */
    public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

2.我们如何来优化校验

上面的代码有以下问题:

  1. Pattern对象被频繁的创建
  2. 我们校验的逻辑写死,代码没法得到通用性,无法复用

我们使用一个静态工厂的这种思想就可以解决这个问题,我们可以把创建好的Pattern对象保存起来,然后利用枚举的特性把校验的正则提前写出来,这我们就解决了上面的2个问题,下面是具体代码:

import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public final class CheckTool {
    private static final ConcurrentHashMap<CheckType, Pattern> PATTERN_MAP = new ConcurrentHashMap<>();

    private CheckTool() {
        throw new RuntimeException("CheckTool Cannot be instantiated!");
    }

    enum CheckType {
        CHECK_NUMBER("\\d+"),
        CHECK_ACCOUNT_NUMBER("^[\\u4e00-\\u9fa5_a-zA-Z0-9]+$"),
        CHECK_CHINES_TEXT_ENGLISH_NUMBERS_AND_UNDERLINE("^[a-zA-Z][a-zA-Z0-9_]{4,15}$"),
        CHECK_IP_ADDRESS("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)"),
        CHECK_CHINESE("^[\\u0391-\\uFFE5]+$"),
        ;
        private String regex;

        CheckType(String regex) {
            this.regex = regex;
        }

        public String getRegex() {
            return regex;
        }

        public void setRegex(String regex) {
            this.regex = regex;
        }
    }

    /**
     * 对外提供一个静态的方法 来处理校验逻辑
     * @param value 需要校验的字符串
     * @param checkType 校验什么类型
     * @return boolean
     * @date 2024/8/1 22:53
     * @since 1.0
     **/
    public static boolean checkValue(String value, CheckType checkType) {
        if (StringUtils.isBlank(value)) {
            throw new IllegalArgumentException("The value to be checked cannot be empty!");
        }
        if (checkType == null || StringUtils.isBlank(checkType.getRegex())) {
            throw new IllegalArgumentException("checkType is not null!");
        }
        Pattern pattern = PATTERN_MAP.computeIfAbsent(checkType, checkPattern -> Pattern.compile(checkPattern.getRegex()));
        return pattern.matcher(value).matches();
    }

    public static void main(String[] args) {
        boolean result = CheckTool.checkValue("123", CheckType.CHECK_ACCOUNT_NUMBER);
        System.out.println("校验结果:" + result);
    }

}

优化思路:

  1. 利用 PATTERN_MAP 来做缓存,对已经创建好的对象做缓存,下次需要校验直接使用即可
  2. 利用 CheckType 把校验的正则抽取出来,让我们这个工具类复用性变得更强
  3. 私有化和不能继承该类,让外面的使用者只能使用 checkValue 这个方法来做校验,避免外部创建无关对象
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值