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.我们如何来优化校验
上面的代码有以下问题:
- Pattern对象被频繁的创建
- 我们校验的逻辑写死,代码没法得到通用性,无法复用
我们使用一个静态工厂的这种思想就可以解决这个问题,我们可以把创建好的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);
}
}
优化思路:
- 利用 PATTERN_MAP 来做缓存,对已经创建好的对象做缓存,下次需要校验直接使用即可
- 利用 CheckType 把校验的正则抽取出来,让我们这个工具类复用性变得更强
- 私有化和不能继承该类,让外面的使用者只能使用 checkValue 这个方法来做校验,避免外部创建无关对象