JAVA续本_Java业务校验工具实现(续集)
一、背景
在前面的文章分享了一篇自已关于Java业务校验工具的实现Java业务校验工具实现,后面本着“不要重复造轮子”的原则,在网上搜索果然有志同道合的朋友已经实现过相同的功能框架fluent-validator。
在大致看完整体功能与大概实现后,觉得这是一个不错的校验框架,但对于我现在的使用场景来说,会感觉有一些“重量级”,即有些功能对我们的使用场景来说有些多余,因为我们的使用场景比较简单直接,即在执行真正的业务逻辑前做业务规则校验,校验不通过则直接返回。本着“简单的就是最好的”原则,所以摘取了fluent-validator的一部分代码写了一个简单版本的fluent-validator,总共类只有6个,FluentValidator.java、Validator.java、ValidatorContext.java、ValidatorElement.java、ValidatorElementList.java、ValidateException.java。以下为全部代码:
二、show me your code
FluentValidator.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 链式调用验证器,参考fluent-validator的简单实现
* https://github.com/neoremind/fluent-validator
*
* @author 会跳舞的机器人
* @date 2019/12/27
*/
public class FluentValidator {
private static final Logger logger = LoggerFactory.getLogger(FluentValidator.class);
/**
* 验证器链,惰性求值期间就是不断的改变这个链表,及时求值期间就是遍历链表依次执行验证
*/
private ValidatorElementList validatorElementList = new ValidatorElementList();
/**
* 验证器上下文
*/
private ValidatorContext context = new ValidatorContext();
/**
* 私有构造方法,只能通过checkAll创建对象
*/
private FluentValidator() {
}
/**
* 创建FluentValidator对象
*
* @return
*/
public static FluentValidator checkAll() {
return new FluentValidator();
}
/**
* 使用验证器进行验证
*
* @param validator 验证器
* @return
*/
public FluentValidator on(Validator validator) {
validatorElementList.add(new ValidatorElement(null, validator));
return this;
}
/**
* 使用验证器验证指定对象
*
* @param t 待验证对象
* @param validator 验证器
* @return
*/
public FluentValidator on(T t, Validator validator) {
validatorElementList.add(new ValidatorElement(t, validator));
return this;
}
/**
* 使用验证器验证指定对象
*
* @param t 待验证对象
* @param validator 验证器
* @param condition 条件,为true时才会将验证器加入验证器列表中
* @return
*/
public FluentValidator on(T t, Validator validator, boolean condition) {
if (condition) {
validatorElementList.add(new ValidatorElement(t, validator));
}
return this;
}
/**
* 执行各个验证器中的验证逻辑
*
* @return
*/
public FluentValidator doValidate() {
if (validatorElementList.isEmpty()) {
logger.info("Nothing to validate");
return null;
}
long start = System.currentTimeMillis();
logger.info("Start to validate,validatorElementList={}", validatorElementList.toString());
String validatorName;
try {
for (ValidatorElement element : validatorElementList.getList()) {
Object target = element.getTarget();
Validator validator = element.getValidator();
validatorName = validator.getClass().getSimpleName();
logger.info("{} is running", validatorName);
validator.validate(context, target);
}
} catch (ValidateException e) {
throw e;
} catch (Exception e) {
throw e;
} finally {
logger.info("End to validate,time consuming {} ms", (System.currentTimeMillis() - start));
}
return this;
}
/**
* 将键值对放入上下文
*
* @param key 键
* @param value 值
* @return FluentValidator
*/
public FluentValidator putAttribute2Context(String key, Object value) {
if (context == null) {
context = new ValidatorContext();
}
context.setAttribute(key, value);
return this;
}
/**
* 获取验证器上下文
*
* @return
*/
public ValidatorContext getContext() {
return context;
}
}
Validator.java
/**
* 验证器接口, 泛型T表示待验证对象的类型
*
* @author 会跳舞的机器人
* @date 2019/12/27
*/
public interface Validator {
/**
* 执行验证,如果验证失败,则抛出ValidateException异常
*
* @param context 验证上下文
* @param t 待验证对象
*/
void validate(ValidatorContext context, T t);
}
ValidatorContext.java
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
/**
* 验证器在执行调用过程中的上下文
* 1.验证器中的数据传递共享
* 2.验证结果数据缓存以作后续使用
*
* @author 会跳舞的机器人
* @date 2019/12/27
*/
public class ValidatorContext {
/**
* 验证器均可以共享使用的属性键值对
*/
private Map attributes;
/**
* 设置属性值
*
* @param key 键
* @param value 值
*/
public void setAttribute(String key, Object value) {
if (attributes == null) {
attributes = new HashMap<>();
}
attributes.put(key, value);
}
/**
* 获取String值
*
* @param key
* @return
*/
public String getString(String key) {
return (String) getAttribute(key);
}
/**
* 获取Integer值
*
* @param key
* @return
*/
public Integer getInteger(String key) {
return (Integer) getAttribute(key);
}
/**
* 获取Boolean值
*
* @param key
* @return
*/
public Boolean getBoolean(String key) {
return (Boolean) getAttribute(key);
}
/**
* 获取Long值
*
* @param key
* @return
*/
public Long getLong(String key) {
return (Long) getAttribute(key);
}
/**
* 获取BigDecimal值
*
* @param key
* @return
*/
public BigDecimal getBigDecimal(String key) {
return (BigDecimal) getAttribute(key);
}
/**
* 获取对象
*
* @param key
* @param
* @return
*/
public T getClazz(String key) {
return (T) getAttribute(key);
}
/**
* 获取属性
*
* @param key 键
* @return 值
*/
public Object getAttribute(String key) {
if (attributes != null && !attributes.isEmpty()) {
return attributes.get(key);
}
return null;
}
}
ValidatorElement.java
/**
* 验证器包装类
*
* @author 会跳舞的机器人
* @date 2019/12/27
*/
public class ValidatorElement {
/**
* 待验证对象
*/
private Object target;
/**
* 验证器
*/
private Validator validator;
public ValidatorElement(Object target, Validator validator) {
this.target = target;
this.validator = validator;
}
public Object getTarget() {
return target;
}
public Validator getValidator() {
return validator;
}
}
ValidatorElementList.java
import java.util.LinkedList;
/**
* 在FluentValidator内部调用使用的验证器链
*
* @author 会跳舞的机器人
* @date 2019/12/27
*/
public class ValidatorElementList {
/**
* 验证器链表
*/
private LinkedList validatorElementLinkedList = new LinkedList<>();
/**
* 将验证器加入链表
*
* @param element
*/
public void add(ValidatorElement element) {
validatorElementLinkedList.add(element);
}
/**
* 获取验证器链表
*
* @return
*/
public LinkedList getList() {
return validatorElementLinkedList;
}
/**
* 验证器链表是否为空
*
* @return
*/
public boolean isEmpty() {
return validatorElementLinkedList.isEmpty();
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for (ValidatorElement element : validatorElementLinkedList) {
sb.append("[");
sb.append(element.getValidator().getClass().getSimpleName());
sb.append("]->");
}
return sb.toString();
}
}
ValidateException.java
/**
* 校验异常
*
* @author 会跳舞的机器人
* @date 2019/4/4
*/
public class ValidateException extends RuntimeException {
// 异常码
private Integer code;
public ValidateException() {
}
public ValidateException(String message) {
super(message);
}
public ValidateException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
三、使用样例
校验器定义
@Component
public class CustomerSignValidator implements Validator {
@Override
public void validate(ValidatorContext context, String s) {
// 模拟获取客户信息并进行校验
Customer customer = new Customer();
if (1 == 2) {
throw new ValidateException("校验客户状态失败");
}
// 将客户信息存入上下文以便后续使用
context.setAttribute("customer", customer);
}
}
使用FluentValidator
// step1.业务规则校验
FluentValidator fluentValidator = FluentValidator.checkAll()
.on(tradeTimeValidator) // 判断交易时间
.on(riskTradeStatusValidator) // 判断交易风控状态
.on(tradeValidateBo, productTradeStatusValidator) // 交易商品状态判断
.on(tradeValidateBo, tradeCountValidator) // 判断交易商品数量是否正确
.on(dto.getCustomerNo(), customerSignValidator) // 判断客户签约开户状态
.on(buyerTradeStatusValidator) // 判断客户交易状态
.on(tradeValidateBo, entrustOrderValidator) // 判断委托单是否合法
.on(tradeValidateBo, buyerBalanceValidator) // 判断买入资金是否足够
.doValidate();
// 从校验器上下文中获取产生的数据
CustomerVo customerVo = fluentValidator.getContext().getClazz("customer");
以上代码即可实现各个业务规则的校验,校验过程中产生的数据可以存放在context上下文中,后续要用到这部分数据可以直接从context中获取。
实际运行例子如下:
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:72] - Start to validate,validatorElementList=[TradeTimeValidator]->[RiskTradeStatusValidator]->[ProductTradeStatusValidator]->[TradeCountValidator]->[PriceValidator]->[CustomerSignValidator]->[BuyerTradeStatusValidator]->[BuyerBalanceValidator]->
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - TradeTimeValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - RiskTradeStatusValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - ProductTradeStatusValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - TradeCountValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - PriceValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - CustomerSignValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - BuyerTradeStatusValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - BuyerBalanceValidator is running
[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:87] - End to validate,time consuming 24 ms
四、总结
以上的校验工具满足了我们当前的需要,我觉得在一般的场景是够用的。另外能想到的扩展场景包括:有需要动态的配置业务校验器的话,可以通过将校验器类名写配置文件中,使用的时候在spring容器中通过类名获取各个校验器实例后,再去执行校验逻辑。如果想要更好的管理各个业务对应的校验器以及校验器的启用与停用状态的话,可以考虑做一个管理界面,在界面中展示某个业务与业务对应的各个校验器,可以开启或者停止校验器。
如果文章对你有帮助的话,给文章点个赞吧。
如果有写得不正确的地方,欢迎指出。
文章首发公众号:会跳舞的机器人,欢迎扫码关注。
JAVA续本_Java业务校验工具实现(续集)相关推荐
- java md5验证工具_Java MD5校验工具类
之前使用到了NIO的FileChannel做文件快速阅读,后来发现存在一个巨大的BUG,使用它会一直不释放文件句柄,即生成MD5的文件不能操作(移动或删除等),这个BUG网上吵得沸沸扬扬,至今没有解决 ...
- 帆软 JAVA扩展_java报表开发工具FineReport教程之报表设计:单元格扩展
java报表开发工具FineReport教程之报表设计:单元格扩展 报表设计章节适用于对报表业务有一定的了解,需要学习FineReport报表的初学者. 通过该章节的学习,可以掌握设计器的使用,了解报 ...
- java 逻辑校验工具_SpringBoot2.0实战(10)整合fluent-validator优雅业务校验
相关知识 FluentValidator是一个工具类库,使用流式(Fluent Interface)调用风格让校验跑起来更优雅,代码更简洁,同时验证器(Validator)可以做到开闭原则,实现最大程 ...
- Java正则表达式校验工具类_【Java工具类】----正则表达式校验工具类
/** * @Title: RegexValidateUtil.java * @Package org.csun.nc.util * @Description: TODO * @author chis ...
- 组织机构代码和统一社会信用代码校验规则以及java校验工具类
组织机构代码 编码规则编辑 1.全国组织机构代码由八位数字(或大写拉丁字母)本体代码和一位数字(或大写拉丁字母)校验码组成. 本体代码采用系列(即分区段)顺序编码方法. 校验码按照以下公式计算: C9 ...
- java身份证号码校验工具类
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...
- Java身份证校验工具类
Java身份证校验工具类 工作中经常会涉及到身份证的校验,而且需求不同,有的需要校验最后一位校验位,有的不需要,这里参考了几篇文章及自己工作中用到的情况写了一个工具类.记录一下,菜鸟一个,大佬勿喷. ...
- JAVA CRC16校验工具
1.工具类 import java.math.BigInteger;/*** CRC校验* @author xl**/ public class CRC16Util {static byte[] cr ...
- Java后端手机号校验工具类
package com.cn.test.tset;import org.apache.commons.lang.StringUtils;import java.util.regex.Pattern;/ ...
最新文章
- 张宏江:人工智能的长远发展需要有人坐冷板凳
- 学习作用域中的“名词”
- 人工智能时代背景下,NLP方向或将悄悄崛起
- windows下可用mysql吗_Windows下MySQL安装配置与使用
- 拖动卡顿_四招教你解决PS软件卡顿问题!
- 使用vue来开发一个下拉菜单组件(2)
- Ubuntu安装之各种坑
- 对策略模式与状态模式的一点思考
- 对象——从现实世界的抽象(*)
- 马云入选全球“十大思想者”,成唯一获选的中国企业家
- js 为对象添加和删除属性
- 大数据Hadoop学习文章汇总
- WiFi的信道与关联
- oracle新增字段 加注释,Oracle数据库表的字段添加注释和向现有表添加字段 | 学步园...
- 解决Ubuntu20.04插入英伟达计算卡后无法开机问题-Ubuntu双显卡切换
- 细胞自噬机制最新研究进展(2021年12月)
- 【板栗糖GIS】如何重复利用arcmap图层的符号样式
- 元素的隐藏和显示(v-show指令)
- 常见C++开源网站项目
- 数据驱动的瑞幸咖啡未来会能赚!