闲暇时间对校验的思考:只为把校验工作“做的像锤子一样简单”(灵感来源于电影AK47诞生中的话《卡拉什尼科夫》,有兴趣可以搜索一下)
实现两行代码快速校验,最终实现需要增加什么校验,加配置刷新缓存即可
版权说明:若有转载和使用请注明出处和原作者,版权所有
基于以上思考,提取维度,不同维度设计不同的策略模式
可根据具体的校验维度来实现不同的策略,业务逻辑主要还是在表达式的处理上和配置上
private String checkClassName;//校验类名
private String checkServiceClassName;//校验服务类名
private String chekWord;//校验对象属性
private String checkexpression;//表达式,可根据具体的业务情况调整
private String checkexpressionOrder; //校验次序
private String checkexpressionDes; //表达式描述
private String checkDimension; //校验维度 用户 代理 通用
private String checkDimensionOrder; //维度顺序
private String checkDimensionWord; //校验维度关键参数 用户的user_id,代理的话代理编号
private String checkModel; //校验模型 非空,长度,
private String checkModelOrder; //校验模式顺序
private String checkModelDes; //校验模式描述
private String checkResult; //校验结果,描述性内容
private String status; //是否生效
private String EffectTimeStatus; //是否设置生效时间段
private String EffectTimeStart; //生效起始时间
private String EffectTimEnd; //结束时间
maven依赖
org.apache.commons commons-lang33.12.0
org.projectlombok lombok1.18.12 provided
项目技术栈:springboot
最终实现方案:数据库配置+redis 实现基于不同维度的策略模式校验
根据维度抽象出最初模型,再根据不同场景抽象出不同策略模型,统一入口和出口
1.整体设计类,真正使用业务代码不两行搞定,底层校验策略规则设计和表达式需要根据不同场景开发测试并配置到数据库
2.对象抽象模型
package com.unique.bean;
import com.unique.service.impl.CompareCheckService;
import com.unique.service.impl.LengthCheckService;
import lombok.Data;
import lombok.SneakyThrows;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
@Data
public class CheckConfig {
private int id;
private String checkClassName;//校验类名
private String checkServiceClassName;//校验服务类名
private String chekWord;//校验对象属性
private String checkexpression;//表达式,可根据具体的业务情况调整
private String checkexpressionOrder; //校验次序
private String checkexpressionDes; //表达式描述
private String checkDimension; //校验维度 用户 代理 通用
private String checkDimensionOrder; //维度顺序
private String checkDimensionWord; //校验维度关键参数 用户的user_id,代理的话代理编号
private String checkModel; //校验模型 非空,长度,
private String checkModelOrder; //校验模式顺序
private String checkModelDes; //校验模式描述
private String checkResult; //校验结果,描述性内容
private String status; //是否生效
private String EffectTimeStatus; //是否设置生效时间段
private String EffectTimeStart; //生效起始时间
private String EffectTimEnd; //结束时间
}
2.校验顶层设计类
package com.unique.service;
import com.unique.bean.CheckConfig;
import java.lang.reflect.Field;
import java.util.Map;
public interface ICheckService {
String checkParam(CheckConfig checkConfig, Object obj, Map mapName);
}
3.工具类
获取对象值
package com.unique.utils;
import com.unique.bean.CheckConfig;
import lombok.SneakyThrows;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
public class CommonUtils {
private CommonUtils(){}
@SneakyThrows
public static String getValue(CheckConfig checkConfig, Object obj, Map mapName) {
Field field = mapName.get(checkConfig.getChekWord());
if (field == null) {
return "pass";
}
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), obj.getClass());
Method readMethod = propertyDescriptor.getReadMethod();
return String.valueOf(readMethod.invoke(obj));
}
}
获取bean
package com.unique.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringBeanUtils implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public Object getBean(String name) {
return applicationContext.getBean(name);
}
public T getBean(Class aclass) {
return applicationContext.getBean(aclass);
}
}
4.校验实现类
非空
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import com.unique.utils.CommonUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.Map;
@Service
@Slf4j
public class EmptyCheckService implements ICheckService {
@SneakyThrows
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
String value = CommonUtils.getValue(checkConfig, obj, mapName);
if ("pass".equals(value)) {
return null;
}
if (value == null || "null".equals(value)) {
return checkConfig.getCheckResult();
}
return null;
}
}
长度
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import com.unique.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.Map;
@Service
@Slf4j
public class LengthCheckService implements ICheckService {
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
String value = CommonUtils.getValue(checkConfig, obj, mapName);
if ("pass".equals(value)) {
return null;
}
String[] split = checkConfig.getCheckexpression().split("\|");
if ("null".equals(value)||checkLowBorder(value, split[1])||checkHighBorder(value, split[1])) {
return checkConfig.getCheckResult();
}
return null;
}
public boolean checkLowBorder(String value, String check_expression) {
if (check_expression == null || value == null || "null".equals(value)) {
return false;
}
try {
if ("(".equals(String.valueOf(check_expression.charAt(0)))) {
if (StringUtils.length(value) <= Integer.parseInt(String.valueOf(check_expression.charAt(1)))) {
return true;
}
} else if ("[".equals(String.valueOf(check_expression.charAt(0)))) {
if (StringUtils.length(value) < Integer.parseInt(String.valueOf(check_expression.charAt(1)))) {
return true;
}
}
} catch (NumberFormatException e) {
log.error("LengthCheckService.checkLowBorder异常:{}", e.getMessage());
return false;
}
return false;
}
public boolean checkHighBorder(String value, String check_expression) {
if (check_expression == null || value == null || "null".equals(value)) {
return false;
}
try {
if (")".equals(String.valueOf(check_expression.charAt(1)))) {
if (StringUtils.length(value) >= Integer.parseInt(String.valueOf(check_expression.charAt(1)))) {
return true;
}
} else if ("]".equals(String.valueOf(check_expression.charAt(1)))) {
if (StringUtils.length(value) > Integer.parseInt(String.valueOf(check_expression.charAt(1)))) {
return true;
}
}
if (check_expression.length() == 5) {
if (")".equals(String.valueOf(check_expression.charAt(4)))) {
if (StringUtils.length(value) >= Integer.parseInt(String.valueOf(check_expression.charAt(3)))) {
return true;
}
} else if ("]".equals(String.valueOf(check_expression.charAt(4)))) {
if (StringUtils.length(value) > Integer.parseInt(String.valueOf(check_expression.charAt(3)))) {
return true;
}
}
}
} catch (NumberFormatException e) {
log.error("LengthCheckService.checkHighBorder异常:{}", e.getMessage());
return false;
}
return false;
}
}
普通替换
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
@Service
public class ReplaceCheckService implements ICheckService {
@SneakyThrows
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
Field field = mapName.get(checkConfig.getChekWord());
if (field == null) {
return "pass";
}
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), obj.getClass());
Method readMethod = propertyDescriptor.getReadMethod();
String value = String.valueOf(readMethod.invoke(obj));
String[] split = checkConfig.getCheckexpression().split("\|");
field.set(obj, checkReplace(value, split[1]));
return null;
}
public static String checkReplace(String value, String expression) {
if (value == null || expression == null) {
return value;
}
String[] splitAll = expression.split(";");
for (String s : splitAll) {
String[] split = s.split(",");
value = value.replace(split[0], split[1]);
}
return value;
}
}
正则替换
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
@Service
@Slf4j
public class ReplaceRegCheckService implements ICheckService {
@SneakyThrows
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
Field field = mapName.get(checkConfig.getChekWord());
if (field == null) {
return "pass";
}
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), obj.getClass());
Method readMethod = propertyDescriptor.getReadMethod();
String value = String.valueOf(readMethod.invoke(obj));
String[] split = checkConfig.getCheckexpression().split("\|");
field.set(obj, checkReplaceReg(value, split[1]));
return null;
}
public static String checkReplaceReg(String value, String expression) {
if (value == null || expression == null) {
return value;
}
String[] splitAll = expression.split(";");
for (String s : splitAll) {
String[] split = s.split(",");
value = value.replaceAll(split[0], split[1]);
}
return value;
}
}
范围比较
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import com.unique.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Map;
@Service
@Slf4j
public class CompareCheckService implements ICheckService {
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
String value = CommonUtils.getValue(checkConfig, obj, mapName);
if ("pass".equals(value)) {
return null;
}
String[] split = checkConfig.getCheckexpression().split("\|");
if ("null".equals(value)||checkLowBorder(value, split[1]) || checkHighBorder(value, split[1])) {
return checkConfig.getCheckResult();
}
return null;
}
public boolean checkLowBorder(String value, String check_expression) {
if (check_expression == null || value == null) {
return false;
}
try {
if ("(".equals(String.valueOf(check_expression.charAt(0)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(1))));
int compare = bigDecimal.compareTo(valueOf);
if (compare <= 0) {
return true;
}
} else if ("[".equals(String.valueOf(check_expression.charAt(0)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(1))));
int compare = bigDecimal.compareTo(valueOf);
if (compare < 0) {
return true;
}
}
} catch (NumberFormatException e) {
log.error("BorderCheckService.checkLowBorder异常:{}", e.getMessage());
return false;
}
return false;
}
public boolean checkHighBorder(String value, String check_expression) {
if (check_expression == null || value == null || "null".equals(value)) {
return false;
}
try {
if (")".equals(String.valueOf(check_expression.charAt(1)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(0))));
int compare = bigDecimal.compareTo(valueOf);
if (compare >= 0) {
return true;
}
} else if ("]".equals(String.valueOf(check_expression.charAt(1)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(0))));
int compare = bigDecimal.compareTo(valueOf);
if (compare > 0) {
return true;
}
}
if (check_expression.length() == 5) {
if (")".equals(String.valueOf(check_expression.charAt(4)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(3))));
int compare = bigDecimal.compareTo(valueOf);
if (compare >= 0) {
return true;
}
} else if ("]".equals(String.valueOf(check_expression.charAt(4)))) {
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(value));
BigDecimal valueOf = BigDecimal.valueOf(Long.parseLong(String.valueOf(check_expression.charAt(3))));
int compare = bigDecimal.compareTo(valueOf);
if (compare > 0) {
return true;
}
}
}
} catch (NumberFormatException e) {
log.error("BorderCheckService.checkHighBorder异常:{}", e.getMessage());
return false;
}
return false;
}
}
包含校验
package com.unique.service.impl;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import com.unique.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class ContainsCheckService implements ICheckService {
@Override
public String checkParam(CheckConfig checkConfig, Object obj, Map mapName) {
String value = CommonUtils.getValue(checkConfig, obj, mapName);
if ("pass".equals(value)) {
return null;
}
String[] split = checkConfig.getCheckexpression().split("\|");
if (checkContains(value, split[2]) && "normal".equals(split[1])) {
return checkConfig.getCheckResult();
}
return null;
}
private static boolean checkContains(String value, String expression) {
if (expression != null) {
List strings = Arrays.asList(expression.split(","));
return strings.stream().anyMatch(keyWord -> value.contains(keyWord));
}
return false;
}
}
5.业务测试类
package com.unique.controller;
import com.unique.bean.CheckConfig;
import com.unique.service.ICheckService;
import com.unique.bean.User;
import com.unique.utils.SpringBeanUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class CheckController {
@Resource
private SpringBeanUtils springBeanUtils;
@RequestMapping(value = "checkParam")
public String checkParam(User user) {
user = new User();
user.setAge(3);
user.setName("123");
user.setAddress("410-6220 Blundel road,*Buzzer 545");
CheckConfig checkConfig = new CheckConfig();
checkConfig.setCheckClassName("class com.unique.bean.User");
checkConfig.setChekWord("name");
checkConfig.setCheckexpression("empty");
checkConfig.setCheckResult("The name is empty");
checkConfig.setCheckModel("empty");
checkConfig.setCheckServiceClassName("com.unique.service.impl.EmptyCheckService");
CheckConfig checkConfig4 = new CheckConfig();
checkConfig4.setCheckClassName("class com.unique.bean.User");
checkConfig4.setChekWord("name");
checkConfig4.setCheckexpression("length|[3,8]");
checkConfig4.setCheckResult("the name is limit [3,8]");
checkConfig4.setCheckModel("length");
checkConfig4.setCheckServiceClassName("com.unique.service.impl.LengthCheckService");
CheckConfig checkConfig2 = new CheckConfig();
checkConfig2.setCheckClassName("class com.unique.bean.User");
checkConfig2.setChekWord("age");
checkConfig2.setCheckexpression("compare|[3,8]");
checkConfig2.setCheckResult("The age is limit (3,8]");
checkConfig2.setCheckModel("compare");
checkConfig2.setCheckServiceClassName("com.unique.service.impl.CompareCheckService");
CheckConfig checkConfig3 = new CheckConfig();
checkConfig3.setCheckClassName("class com.unique.bean.User");
checkConfig3.setChekWord("address");
checkConfig3.setCheckexpression("replace|*,,null;-, ;"); // 如果替换为空串,则表达式需要多一个逗号分割,最后一个
checkConfig3.setCheckModel("replace");
checkConfig3.setCheckServiceClassName("com.unique.service.impl.ReplaceCheckService");
CheckConfig checkConfig6 = new CheckConfig();
checkConfig6.setCheckClassName("class com.unique.bean.User");
checkConfig6.setChekWord("address");
checkConfig6.setCheckexpression("replaceReg|[^a-zA-Z ]+,,null;");//多个正则替换以分号分割,如果要替换为空串,逗号分割部分后加上一个非空数据
checkConfig6.setCheckModel("replaceReg");
checkConfig6.setCheckServiceClassName("com.unique.service.impl.ReplaceRegCheckService");
CheckConfig checkConfig5 = new CheckConfig();
checkConfig5.setCheckClassName("class com.unique.bean.User");
checkConfig5.setChekWord("address");
checkConfig5.setCheckexpression("contains|normal|Blundel,Road");// special模式下 替换大写,替换成小写,正则替换
checkConfig5.setCheckResult("The address cannot contain Blundel,Road");
checkConfig5.setCheckModel("contains");
checkConfig5.setCheckServiceClassName("com.unique.service.impl.ContainsCheckService");
List checkConfigs = new ArrayList<>();
checkConfigs.add(checkConfig);
checkConfigs.add(checkConfig2);
checkConfigs.add(checkConfig3);
checkConfigs.add(checkConfig4);
checkConfigs.add(checkConfig5);
checkConfigs.add(checkConfig6);
Map mapName = getStringFieldMap(user);
String s = null;
try {
s = checkParam(checkConfigs, user, mapName);
} catch (Exception e) {
log.error("校验异常:{}", e.getMessage());
}
log.info("对象:{}",user);
if (s != null) {
return s;
}
return null;
}
private Map getStringFieldMap(Object obj) {
Map mapName = new HashMap<>();
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
mapName.put(declaredField.getName(), declaredField);
}
return mapName;
}
@SneakyThrows
private String checkParam(List checkConfigs, User user, Map mapName) {
if (checkConfigs != null && checkConfigs.size() > 0) {
for (CheckConfig checkConfig : checkConfigs) {
if (StringUtils.isNotEmpty(checkConfig.getCheckServiceClassName())) {
Class> aClass = Class.forName(checkConfig.getCheckServiceClassName());
if (String.valueOf(user.getClass()).equals(checkConfig.getCheckClassName())) {
log.info("校验类匹配:{}",String.valueOf(user.getClass()).equals(checkConfig.getCheckClassName()));
}
ICheckService bean = (ICheckService) springBeanUtils.getBean(aClass);
String s = bean.checkParam(checkConfig, user, mapName);
if (s != null) {
return s;
}
}
}
}
return null;
}
}
验证结果
http://localhost:9002/checkParam
The address cannot contain Blundel,Road



