Java工具类——通过配置XML验证Map

Java工具类——通过配置XML验证Map

背景

在JavaWeb项目中,接收前端过来的参数时通常是使用我们的实体类进行接收的。但是呢,我们不能去决定已经搭建好的框架是怎么样的,在我接触的框架中有一种就是通过Map来接收前端过来的所有参数,框架中没有实体类的说法,从接收参数,验证参数到参数至持久层整个过程都是通过Map来传递数据。

而在开发的过程中,减少了实体类的存在,有时是感觉挺方便的,比如:一个系统中有100多个表,这里我们可以减少工作量(虽然对应表的实体可以代码生成),因为我们开发过程中是需要返回多个表关联后的结果的,这里可能我们需要创建DTO,这些步骤确实是挺烦人的。但是,前端过来的参数我们需不需要验证呢?客户的输入不管有意或者是无意,我认为都应该让系统的容错能力更强悍一些。所以,在验证前端过来的参数时,使用了Map就着实让人头痛。每个需要强制验证的参数都需要get,然后判断类型,强制转型,判断参数符不符合期望值边界等。

所以,我就考虑了,实体类可以通过Spring MVCHibernateValidation使用注解的方式进行参数校验,那么,少了实体类,我是不是可以通过配置XML的方式来达到类似有实体类的效果。网上找了类似关键词的工具类,发现没有我所期望的,所以就动手来了一个。

大致的想法

Web开发时,有许多if-else语句的出现都是在为了验证前端参数合不合法真的是挺无奈的,而且有些代码虽然长起来类似但是呢要去重构成一个公用的方法好像有些困难,时常问自己,要怎么去搞,Java不是JavaScript,语句没那么灵活。

于是想着通过XML配置试试,大致就是通过配置好的XML代替我们的实体类,并且有个入口将XML中的实体映射,并传入待验证的Map,验证之后传出一个数组,如果验证通过数组为空,不通过则是我们XML中配置的对应错误语句。

如何设计XML格式

动手在这之前,需要想好我们大致的XML结构是怎么样的。这里,我的想法是,在我们一般遇到的参数主要就是IntegerStringDoubleDateList了(这里居然没有考虑Boolean,算了,之后再做补充也行)。所以基于以上,设计的结构大致如下:

<?xml version="1.0" encoding="UTF-8"?>
<map-verifyxmlns="https://www.lger.cn/verify"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://www.lger.cn/verify map-verify-util.xsd"><!--在长度的方面分别使用了lt; lte; gt; gte; eq;分别代表<; <=; >; >=; =--><Entity name="User01"><!--castErrMsg为当验证过程中类型解析错误时返回提示--><String name="username" castErrMsg="username必须为字符串类型"><length><lt errMsg="当前值不能小于2">2</lt></length><notNull errMsg="当前值不能为空"/><notBlank errMsg="当前值不能为空(去掉首尾空格)"/><pattern><!--如果不匹配则发出错误--><value errMsg="号码D[0-8]{4}">[0-8]{4}</value><value errMsg="号码D[0-7]{4}">[0-7]{4}</value></pattern></String><Integer name="age" castErrMsg="必须为整型"><lt errMsg="岁数必须大于2">2</lt><gt>150</gt><notNull/></Integer><List name="details"><notEmpty/><size><gte>0</gte></size><!-- 遍历List内容,遍历的Entity映射为name为User02的实体 --><forEach entity="User02" /></List><Date name="birthday"><lt>1900-07-21</lt><notNull/></Date><Double name="money"><lt>1</lt><gt>150</gt><notNull/></Double></Entity><Entity name="User02"><String name="username"><length><lt errMsg="当前值不能小于2">2</lt><gt errMsg="当前值不能大于10">10</gt></length><notBlank errMsg="当前值不能为空(去掉空格)"/></String></Entity>
</map-verify>

需要写出一个具有一定格式的XML那么还需要写出一个约束文件了验证是否XML格式写的正确。这里还小学了一下XSD,这里就不贴出代码了。

以上的XML大致的说了下怎么使用其替代实体类。写到了这里其实有时还觉得使用实体类可能更方便,何必使用Map呢?但是,别人框架已经写了,参数只能接收到Map那我只能屈服了。

类结构设计

首先,每一个Entity就是一个实体对象,这里我认为每个Entity都应该包含有一个验证方法和一个初始化方法,因为在进行XML解析时就调用init,在进行Map验证时就调用verify方法这样,那些String节点也是类似的,解析初始化时就把XML中配置的信息保存起来,等到验证时就通过之前保存的信息进行判断即可,不必重新解析了。

这里以解析一个IntegerEntity为例,首先是其父类,不论是XML中哪个节点,都是实现VerifyEntity接口,代码如下:

public interface VerifyEntity {/*** 一个实体初始化,当实体被创建时将由创建方主动调用* @param currentEle 被创建的实体节点* @param factory 实体创建工厂,可以通过此工厂创建Entity*/void init(Element currentEle, EntityFactory factory);/*** 验证当前节点是否匹配XML的配置* @param value 需要验证的值* @return 异常字符串集*/String[] verify(Object value);/*** 初始化完毕后调用,传入包含所有Entity的Map* @param entityMap entityMap*/void finished(Map<String, VerifyEntity> entityMap);}

首先解析XML时当获取到Entity节点下的子节点将会通过一个工厂类创建子节点的对应实现VerifyEntity,之后调用init方法对当前的子节点进行解析。这里先看下IntegerEntity的源码:

public class IntegerEntity implements VerifyEntity {private AbstractEquation<Integer> integerEquation;private boolean notNull = false;private String notNullErrMsg;private String castErrMsg;@Overridepublic void init(Element currentEle, EntityFactory factory) {// 开始解析当前节点<Integer/>String name = currentEle.attributeValue("name");// 获取节点属性castErrMsg,看是否存在castErrMsgthis.castErrMsg = currentEle.attributeValue("castErrMsg");if (Util.isEmpty(this.castErrMsg)) {this.castErrMsg = name + ": this is not integer type.";} else {this.castErrMsg = name + ": " + this.castErrMsg;}// 获取子节点notNullElement element = currentEle.element("notNull");if (element != null) {this.notNull = true;this.notNullErrMsg = element.attributeValue("errMsg");if (Util.isEmpty(this.notNullErrMsg)) {this.notNullErrMsg = name + ": this is not null";} else {this.notNullErrMsg = name + ": " + this.notNullErrMsg;}}// 这里是新建一个抽象的Equation类,主要是因为lt和lte等等的这些节点在其他实体中也有,为了代码复用所以使用了抽象类来定义//这里实现后与上面解析notNull代码差异不大integerEquation = new AbstractEquation<Integer>(currentEle, name) {@OverrideInteger valueOf(String value) {try {return Integer.valueOf(value);}catch (NumberFormatException e) {throw new ConvertException(castErrMsg);}}@Overrideboolean lessThan(Integer value, Integer lt) {return value < lt;}@Overrideboolean greaterThan(Integer value, Integer gt) {return value > gt;}@Overrideboolean lessThanOrEquals(Integer value, Integer lte) {return value <= lte;}@Overrideboolean greaterThanOrEquals(Integer value, Integer gte) {return value <= gte;}@Overrideboolean equals(Integer value, Integer eq) {return value.equals(eq);}};}@Overridepublic String[] verify(Object value) {//正式验证参数是否合法if (value == null) {//如果之前解析包含notNull,则这里为true,那么将返回解析的notNullErrMsgif (this.notNull) {return new String[]{this.notNullErrMsg};}return null;}try {//使用上面实现的抽象类进行验证return integerEquation.verify(Integer.valueOf(value.toString()));}catch (NumberFormatException e) {return new String[]{this.castErrMsg};}}@Overridepublic void finished(Map<String, VerifyEntity> entityMap) {//这里是在Entity解析完毕后调用,并将保存Entity的Map传入}}

其实根据以上的代码可以看出,就是XML的节点对应着一个VerifyEntity实现,每一个实现都保存着其中定义的<notNull/>等信息。

实现后运行效果

这里我们的测试代码如下,其中demo.xml设计XML所示的代码:

        //初始化MapVerify,将定义好xml以数组方式传入final String[] xmls = {"demo.xml"};MapVerify.init(xmls);final Map<String, Object> map = new HashMap<>(5);map.put("username", "12312371237123778");map.put("age", 1);map.put("birthday", "2000-07-21");map.put("money", 149);List<Map<String, Object>> list = new ArrayList<>(2);Map<String, Object> map1 = new HashMap<>(1);map1.put("username", "abcab");Map<String, Object> map2 = new HashMap<>(1);map2.put("username", "cc");list.add(map1);list.add(map2);map.put("details", list);System.out.println(Arrays.toString(MapVerify.verifyByReturnArr("User01", map)));

运行的结果如下:

[age: 岁数必须大于2]

总结

此想法是半年前的了,现在利用了空余的时间实现了自己的想法,趁热打铁写下博客,希望有这个需求的小伙伴能节省自己的时间(注:这里Boolean的判断没有实现,如果有需要就需要自己动手了:-))。以后也希望自己如果有什么小想法尽量的抽时间去做,不论做的好不好。

源码已经上传至 GitHub,包含demo。
jar包请点击 此链接,使用jar包时需要多引入dom4j依赖包

转载于:https://www.cnblogs.com/lger/p/10645453.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/264357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言用指针两个字母交换,c语言指针基础之用指针交换两个数(代码实例)

用指针交换两个数&#xff1a;void swap(int *p,int *q) {int temp;temp *p;*p *q;*q temp;}int main(){int a 3, *p,c 5, *q;p &a; //把变量a的地址赋值给指针p&#xff0c;即p指向aq &c;swap(p,q);printf("a %d,c %d\n", a, c);return 0;}注意&am…

如何在 Windows Server 2003、Windows 2000 和 Windows XP 中备份恢复代理的加密文件系统 (EFS) 私钥...

本 文介绍了如何在运行 Microsoft Windows Server 2003、Microsoft Windows 2000 或 Microsoft Windows XP 的计算机上备份恢复代理加密文件系统 (EFS) 私钥。当位于本地计算机上的 EFS 私钥副本丢失时&#xff0c;请使用恢复代理的私钥恢复数据。本文包含有关如何使用证书导出…

你可能从未听过的 Linux 发行版

Hanthana Linux 官方主页&#xff1a;http://www.hanthana.org Hanthana Linux 基于 Fedora&#xff0c;主要面向 IT 教育&#xff0c;默认包含额外的编/解码器及多媒体播放器。它提供可安装到硬盘的 LiveDVD&#xff0c;支持 i686 和 x86_64 架构。 ROSA Linux 官方主页&#…

从壹开始 [vueAdmin后台] 之三 || 动态路由配置 项目快速开发

回顾 今天VS 2019正式发布&#xff0c;实验一波&#xff0c;你安装了么&#xff1f;Blog.Core 预计今天会升级到 Core 3.0 版本。 哈喽大家周三好&#xff01;本来今天呢要写 Id4 了&#xff0c;但是写到了一半&#xff0c;突然有人问到了关于 Blog.Admin 管理后台的一些问题&a…

c语言支持默认参数吗,嵌入式C语言可以带“默认参数”的函数吗

描述(文章来源&#xff1a;嵌入式时代)使用C开发过程序时&#xff0c;定义函数可以指定默认参数&#xff0c;例如 void fun(int x, int y3); 在调用 fun() 时第二个参数可以不传递&#xff0c;此时 fun() 函数默认第二个参数等于 3&#xff0c;例如 f(1) 就相当于 f(1,3)。这是…

《从零开始学习ASP.NET MVC 1.0》-开天辟地入门篇

《从零开始学习ASP.NET MVC 1.0》 文章导航 (一) 开天辟地入门篇 (二) 识别URL的Routing组件 (三) Controller/Action 深入解析与应用实例 (四) View/Model 全解 (五) ViewEngine 深入解析与应用实例 一.摘要 随着ASP.NET MVC 1.0版本的正式发布, 我将本系列文章也更新到了1.0,…

制作放两个小图片的按钮 - 回复 xhui 的问题

为什么80%的码农都做不了架构师&#xff1f;>>> 问题来源: http://www.cnblogs.com/del/archive/2009/03/12/1409708.html#1475240 本例效果图: 自定义的类(TMyButton): unit Unit2;interfaceusesWindows, Messages, Classes, Graphics, StdCtrls;typeTMyButton c…

ajax传递数组,后台更新

js&#xff1a; var rows $("#stu_reg_table").datagrid("getSelections");if(rows ! ""){$.messager.confirm("系统确认","您确认同意该申请吗&#xff1f;",function (r) {if(r){var id[];for(i0;i<rows.length;i){id…

c语言 指针 pdf,深入理解c指针 PDF扫描版[33MB]

深入理解C指针 内容简介&#xff1a;深入理解C指针和内存管理&#xff0c;提升编程效率&#xff01;这是一本实战型图书&#xff0c;通过它&#xff0c;读者可以掌握指针动态操控内存的机制、对数据结构的增强支持&#xff0c;以及访问硬件等技术。本书详细阐述了如何在数组、字…

使用SQL Server 2008提供的表分区向导

表分区&#xff08;Partition Table)是自从SQL Server 2005就开始提供的功能&#xff0c;解决的问题是大型表的存储和查询。 我们之前大致的语法是这样的 -- -- 演示&#xff1a;陈希章 -- 如何创建分区函数 -- 如何创建分区架构 -- 如何创建分区表 -- alter database adventu…

唤醒控件曾经拥有的能力

为什么80%的码农都做不了架构师&#xff1f;>>> 控件的祖先 TControl 有很多功能, 但它的有些子孙确丧失了很多(为了专用). 譬如 TBevel 就没有把一些常规的事件继承下来, 那些没有被继承的功能一般都隐藏在 protected 区, 如果重新继承是可以使用的. 这里尝试了另…

Android 面向切面编程-aspjectj应用

先附上自己基于aspject封装的aop 插件 1.为什么要用切面 随着业务越来越复杂&#xff0c;项目中的模块可能越来越多&#xff0c; 面向切面可以减少模块间的耦合&#xff0c;提高模块的复用率 2.aspjectj语法 android aop框架 有好几种&#xff0c;不过aspjectj应该算最成熟了&…

c语言掌上通,计算机二级C语言掌上通在哪下载安装?计算机掌上通好用吗?

计算机二级C语言掌上通在哪下载安装&#xff1f;计算机二级C语言掌上通好用吗&#xff1f;相信很多想要考计算机二级C语言的学生都在努力的复习背题库&#xff0c;而现在只要使用计算机二级C语言掌上通就能够随时随地刷题了。一、计算机二级C语言掌上通怎么下载1、滑动手机&…

[原]RHCS集群的服务切换测试札记

估计使用Red Hat或者CentOS做HA集群的朋友多数都会选择RedHat Cluster Suite&#xff08;RHCS&#xff09;这个套件来做吧。本篇主要记录构建及测试时候的情况。 poweroff 和 reboot 这种常规操作的服务切换取决于 recovery"relocate" 这个参数&#xff0c;在图形化界…

理解 Delphi 的类(十) - 深入方法[17] - 提前声明

为什么80%的码农都做不了架构师&#xff1f;>>> //要点17: 如果前面的方法要调用后面的方法, 后面的方法需要提前声明 function MyFunB(x: Integer): Integer; forward; {使用 forward 指示字提前声明}function MyFunA(x: Integer): Integer; beginResult : MyFunB…

本地仓库推送到远程仓库:fatal: refusing to merge unrelated histories

最近&#xff0c;在操作git的时候&#xff0c;遇到各种问题&#xff0c;下面总结一下。 最开始&#xff0c;我不是先把远程仓库拉取到本地 &#xff0c;而是直接在本地先创建一个仓库&#xff0c;再git remote add添加远程仓库。 当然&#xff0c;gitee官方还是有操作指南&…

c语言全段字符的类别,言语理解规律整理d0c.doc

言语理解规律整理d0cAUTOTEXT "第 X 页 共 Y 页" \* MERGEFORMAT 第 PAGE 14 页 共 NUMPAGES 14 页言语理解规律整理言语理解之广义主旨题 :  主要的提问方式有&#xff1a;“主要说明、主要意思、主要谈论、主要讲述、核心意思、主要表达、主要告诉我们”这些表达…

小小闹钟wpf版

以前做过一个vc版的&#xff0c;最近看了一下wpf&#xff0c;于是有在这个模式下重写了程序&#xff0c;算是一个练习&#xff0c;工程下载地址&#xff1a;http://cid-fbe22b5b41948219.skydrive.live.com/self.aspx/.Documents/clock.rar转载于:https://www.cnblogs.com/hxf8…

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

在上篇随笔《C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理》介绍了通讯录的部门的相关操作管理&#xff0c;通讯录管理包括部门管理、成员管理、标签管理三个部分&#xff0c;本篇主要介绍成员的管理操作&#xff0c;包括创建、删除、更新、获取、获取部门成…

IDEA 运行spingboot时出现Process finished with exit code -1073741819 (0xC0000005)

经过多方查证&#xff0c;问题最终定位在金山词霸2016上&#xff0c;如果开启了金山词霸的取词和划意功能&#xff0c;就会出现此错误&#xff0c;估计是冲突吧。 解决办法&#xff1a;关掉金山词霸&#xff0c;或者把金山词霸的取词和划意功能关掉。经过尝试&#xff0c;发现只…