Java 简单校验框架
数据校验框架现状
在我们的方法入口后面,难免会有如下样子的代码:
result.setSuccess(false);
if (StringUtils.isBlank(bizOrder.getThirdOrder())) {
result.setResultMessage("thirdOrder不能为空");
return result;
}
if(bizOrder.getThirdOrder().length() > 100){
result.setResultMessage("thirdOrder长多过长,必须在100以内");
return result;
}
if (StringUtils.isBlank(bizOrder.getSku())) {
result.setResultMessage("sku不能为空");
return result;
}
if (StringUtils.isBlank(bizOrder.getName())) {
result.setResultMessage("name不能为空");
return result;
}
if(bizOrder.getName().length() > 20){
result.setResultMessage("name字数过长");
return result;
}
if (bizOrder.getProvince() == 0 || bizOrder.getCity() == 0
|| bizOrder.getCounty() == 0) {
result.setResultMessage("地址信息不正确");
return result;
}
if (StringUtils.isBlank(bizOrder.getAddress())) {
result.setResultMessage("address不能为空");
return result;
}
对于一名有洁癖的程序员,这显然是不行的,我们要更加的优雅。
好吧,马后炮了,其实早就有这样的规范了:JSR 303 - Bean Validation
对于其实现,目前用的最广泛的是:Hibernate Validator
Hiberante Validator, 小巧,规范,易扩展,易整合。
但是本文不是说它。。。
对于Web应用,可能更多的我们还是使用Spring MVC的校验,叫做:spring mvc validator
一百度一大堆,可以跟页面的error标签很好的结合做页面输入的校验。
但是本文也不是说它。。。
本文主要是说,来写一个适合自己的校验框架
数据框架设计目的
- 要简单
只是作为一个小的工具包,代码最多几K,无依赖也是必须的吧 - 要优雅
if.else的调用方式太难看了。看看如下的这种怎么样:
new Validator().notNull(name, "姓名").notNull(mail, "邮箱");
- 1
- 要易用
注解是易用的一个好办法,就像JSR303那样 - 要可扩展
要方便客户端程序方便的创建自定义校验器
总体设计
首先得起个名字吧,叫MiniValidator
主要分了两个部分:
1. 用来给对象进行注解的Annotation及其解析器和校验器
Annotation ,一组注解
Parser, 注解解析器,主要处理注解的行为
AnnotationValidator 使用注解和解析器对传入的对象的字段进行校验
2. 可扩展的校验器
AnnotationRule 注解校验rule,作为内置的rule使用
Rule 用于扩展,可以自定义Rule
Validator 使用Rule对数据进行校验,或者使用内置的校验器
实现
注解校验部分
首先写一个注解, 例如不能为空白:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotBlank {
public String fieldName();
}
然后是对应该注解的解析器:
/**
* 不能为空白校验器
* @author cdwangzijian
*
*/
public class NotBlankParser implements IAnnotationParser {
/**
* 校验字段f的值不能为null或者是空字符串,校验结果保存在result中
*/
@Override
public ValidateResult validate(Field f, Object value) {
ValidateResult result = new ValidateResult();
if(f.isAnnotationPresent(NotBlank.class)){
NotBlank notBlank = f.getAnnotation(NotBlank.class);
if(value == null || value.toString().length() == 0){
result.setMessage(notBlank.fieldName() + "不能为空");
}
}
return result;
}
}
下面是使用上面内容的注解校验器:
/**
* 注解校验器
* @author cdwangzijian
*
*/
public class AnnotationValidator {
private static final Logger log = Logger.getLogger(AnnotationValidator.class.getName());
private final static List<IAnnotationParser> vList = new ArrayList<IAnnotationParser>();
static {
vList.add(new NotNullParser());
vList.add(new NotBlankParser());
}
/**
* 遍历所有字段,用所有解析器进行校验,如果校验失败,则终止校验返回结果,如果校验成功,同样返回校验结果
* @param t
* @return
*/
public static <T> ValidateResult validate(T t){
ValidateResult result = null;
for (Field f : t.getClass().getDeclaredFields()) {
f.setAccessible(true);
Object value = null;
try {
value = f.get(t);
} catch (IllegalArgumentException e) {
log.log(Level.SEVERE, "Exception", e);
} catch (IllegalAccessException e) {
log.log(Level.SEVERE, "Exception", e);
}
for (IAnnotationParser va : vList) {
result = va.validate(f, value);
if(!result.isValid()){
return result;
}
}
}
return result;
}
/**
* 注册解析器
* @param parser
*/
public static void register(IAnnotationParser parser){
vList.add(parser);
}
}
可以看到该校验器已经注册了多个解析器。然后对于传入的对象,会对每一个字段的值进行所有解析器的校验,得到校验结果。
写一个测试程序吧:
class User{
private Long id;
@NotBlank(fieldName="姓名")
private String name;
@Less(fieldName="年龄", value=100)
private int age;
private String phone;
private String birthday;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
}
public class TestAnnotationValidator {
public static void main(String[] args) {
User user = new User();
ValidateResult result = AnnotationValidator.validate(user);
if(result.isValid()){
System.out.println("校验通过");
}else{
System.out.println(result.getMessage());
}
}
}
输出的结果:
姓名不能为空
- 1
扩展注解校验器
基于这个框架,还是可以比较方便的进行扩展的。
要写一个新的注解,新的解析器,然后注册一下新的解析器就能给新的字段进行校验了。如下:
新的注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DateFormat {
public String fieldName();
public String format();
}
- 1
- 2
- 3
- 4
- 5
- 6
新的解析器
/**
* 日期格式注解解析器
*
* @author cdwangzijian
*
*/
public class DateFormatParser implements IAnnotationParser{
/**
* 校验f字段的值是否符合value的日期格式
* @see DateFormat
*/
@Override
public ValidateResult validate(Field f, Object value) {
ValidateResult result = new ValidateResult();
if(f.isAnnotationPresent(DateFormat.class)){
DateFormat dateFormat = f.getAnnotation(DateFormat.class);
try {
if(value != null){
SimpleDateFormat format = new SimpleDateFormat(dateFormat.format());
format.parse(value.toString());
}
} catch (ParseException e) {
result.setMessage(dateFormat.fieldName() + "不满足格式:" + dateFormat.format());
}
}
return result;
}
}
使用扩展注解的测试程序:
public class TestAnnotationValidator {
public static void main(String[] args) {
User user = new User();
user.setName("wzj");
user.setAge(21);
user.setBirthday("20150525");
AnnotationValidator.register(new DateFormatParser());
ValidateResult result = AnnotationValidator.validate(user);
if(result.isValid()){
System.out.println("校验通过");
}else{
System.out.println(result.getMessage());
}
}
}
结果:
生日不满足格式:yyyy-MM-dd
好了,注解的部分就这么多了。
通用校验部分
通用校验部分首先是一个接口Rule, 供给校验器调用:
/**
* 校验规则,用于扩展校验规则
* @author cdwangzijian
*
*/
public interface Rule {
public String getMessage();
public boolean isValid();
}
使用Rule的校验器:
/**
* 通用校验器
* @author cdwangzijian
*
*/
public class Validator {
public Validator validate(Rule rule) {
if(this.isValid){
this.isValid = rule.isValid();
this.message = rule.getMessage();
}
return this;
}
public Validator validateAnnotation(Object o){
return validate(new AnnotationRule(o));
}
public Validator notNull(Object fieldValue, String fieldName) {
if(this.isValid){
if(fieldValue == null){
this.isValid = false;
this.message = fieldName + "不能为空";
}
}
return this;
}
/**
* 是否有效
* @return
* true 校验通过,值有效
* message 校验未通过的错误信息
*/
public boolean isValid() {
return isValid;
}
public String getMessage() {
return message;
}
private boolean isValid = false; // 是否有效
private String message; // 错误信息
}
该类除了使用Rule以外,还内置了一些notXX的方法,返回this,这样可以用.notXX().notXX().notXX()的结构来进行校验。
来测试一下:
public class TestValidator {
public static void main(String[] args) {
testMethod("name", null, null, null);
}
public static void testMethod(String name, String mail, String thirdOrderId, String address){
Validator v = new Validator().notNull(name, "姓名").notNull(mail, "邮箱").notNull(address, "地址");
if(v.isValid()){
System.out.println("校验通过");
}else{
System.out.println(v.getMessage());
}
}
}
结果:
邮箱不能为空
扩展通用校验器
扩展就需要实现Rule接口,如下我们实现一个基于AnnotationValidator的Rule:
/**
* 使用AnnotationValidator的校验规则
*
* @see AnnotationValidator
* @author cdwangzijian
*
*/
public class AnnotationRule implements Rule{
private String message;
private Object o;
public AnnotationRule(Object o) {
this.o = o;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isValid() {
ValidateResult result = AnnotationValidator.validate(this.o);
this.message = result.getMessage();
return result.isValid();
}
}
然后在测试中使用中rule:
public class TestValidator {
public static void main(String[] args) {
new Validator().validate(new AnnotationRule(new User()));
}
}
- 1
- 2
- 3
- 4
- 5
总结
到此,这个简单的校验框架就完成了。
主要的技术上使用了注解,
然后通过反射再利用注解解析器来进行解析进行校验
校验器每个方法返回this,可以使用更优雅的代码来完成校验
并且还可以比较方便的扩展。
完整的代码,可以从这里获取
原文地址:https://blog.csdn.net/three_man/article/details/46046779
Java 简单校验框架的更多相关文章
- Java对象校验框架之Oval
只要有接口,就会有参数的校验,目前开源的校验框架已经非常多了,不过不得不提一下Oval.OVal 是一个可扩展的Java对象数据验证框架,验证的规则可以通过配置文件.Annotation.POJO ...
- 自己写的基于java Annotation(注解)的数据校验框架
JavaEE6中提供了基于java Annotation(注解)的Bean校验框架,Hibernate也有类似的基于Annotation的数据校验功能,我在工作中,产品也经常需要使 用数据校验,为了方 ...
- java简单框架设计
设计框架包可以作为一个工具给大家用,需要有完全不同设计思路给出来,不同于我们去做一个web服务.网站. 或者一个业务微服务,需要从原来使用视角转换成一个构建者视角. 框架或者工具,更多是框架来管理或者 ...
- 使用Java元注解和反射实现简单MVC框架
Springmvc的核心是DispatcherServlet来进行各种请求的拦截,进而进行后续的各种转发处理.流程图如下: 说明:客户端发出一个http请求给web服务器,web服务器对http请求进 ...
- Java实现简单RPC框架(转)
一.RPC简介 RPC,全称Remote Procedure Call, 即远程过程调用,它是一个计算机通信协议.它允许像本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).H ...
- Struts2中的校验框架
Struts2提供的客户端校验 尽管这种支持比较弱,但采用Struts2中的客户端校验时需要注意以下几点 1..将<s:form validate="true">的va ...
- Java中的框架基础面试知识
spring mvc 工作机制(原理): DispatcherServlet主要用作职责调度工作,本身主要用于控制流程 Spring mvc运行原理 1.springmvc将所有的请求都提交给Disp ...
- java自动化-juint框架简述
本人使用的是java的juint框架来组织的自动化测试,故我这边需要简单介绍一下juint框架 首先,建议自行百度一下juint框架,先有一个大概的了解 所谓的接口自动化测试,会对多个接口中每一个接口 ...
- java自动化-junit框架简述
本人使用的是java的junit框架来组织的自动化测试,故我这边需要简单介绍一下junit框架 首先,建议自行百度一下junit框架,先有一个大概的了解 所谓的接口自动化测试,会对多个接口中每一个接口 ...
随机推荐
- 洛谷P1080 [NOIP2012提高组D1T2]国王游戏 [2017年5月计划 清北学堂51精英班Day1]
P1080 国王游戏 题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 ...
- js实现使用递归来计算1~任意数字的和
function getSum(n){ if(n==1){ return 1; } return n+getSum(n-1); } var res = getSum(100); console.log ...
- hdu 3791 二叉搜索树(数据结构)
二叉搜索树 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- 从0开始学习 GitHub 系列之「04.向GitHub 提交代码」
之前的这篇文章「从0开始学习 GitHub 系列之「Git速成」」相信大家都已经对 Git 的基本操作熟悉了,但是这篇文章只介绍了对本地 Git 仓库的基本操作,今天我就来介绍下如何跟远程仓库一起协作 ...
- css中background背景属性概述
background:url(背景图片路径) no-repeat;/*不重复默认在左上方*/ background:url(背景图片路径) no-repeat center;/*不重复背景图片中间显示 ...
- 2017年Android SDK下载安装及配置教程(附带原文地址)
首先声明: Unity版本5.6.3f1 最近试着在Unity中利用高通做AR开发时,发布项目文件需要发布到Android平台,遇到一些问题,看了网上的一些资料,踩了一些坑,现在总结出来,希望有相同的 ...
- JavaScript 数字滚动countup.js
1. 概述 1.1 说明 在项目过程中,有时候需要动态的去展示一些数据的加载状态,如一个数字为10000,需要5秒时间滚动加载完成.此时使用countup.js就能够很方便的处理此类功能问题. 1.2 ...
- 计算机网络.{wireshark的使用实验}
---恢复内容开始--- 三种选择 1 2 3 端口镜像:交换机的某个端口,接受或者发送的数据给另外一个端口 ARP欺骗: ARP欺骗是利用ARP协议自身的不足进行的欺骗 1 执行ping命令, 2 ...
- phpcms 允许英文目录有空格
大家都用过phpcm添加栏目吧,在添加栏目里面,有个选项是 英文目录,这里目录可以用作伪静态功能.这么英文不能有空格等特殊字符.但是如果页面中需要引用包含空格的字符呢,例如,关于我们页面,我要显示英文 ...
- 公司电脑安装mysql出现小问题
按步骤将mysql安装好后,在自己电脑完全没问题,但是在公司电脑安装的时候出现了这样的问题. 查阅资料以后,找到了问题: 参考链接:https://blog.csdn.net/huacode/arti ...