在JavaWeb項目中,接收前端過來的參數時一般是使用咱們的實體類進行接收的。可是呢,咱們不能去決定已經搭建好的框架是怎麼樣的,在我接觸的框架中有一種就是經過Map來接收前端過來的全部參數,框架中沒有實體類的說法,從接收參數,驗證參數到參數至持久層整個過程都是經過Map來傳遞數據。前端
而在開發的過程當中,減小了實體類的存在,有時是感受挺方便的,好比:一個系統中有100多個表,這裏咱們能夠減小工做量(雖然對應表的實體能夠代碼生成),由於咱們開發過程當中是須要返回多個表關聯後的結果的,這裏可能咱們須要建立DTO
,這些步驟確實是挺煩人的。可是,前端過來的參數咱們需不須要驗證呢?客戶的輸入無論有意或者是無心,我認爲都應該讓系統的容錯能力更強悍一些。因此,在驗證前端過來的參數時,使用了Map
就着實讓人頭痛。每一個須要強制驗證的參數都須要get
,而後判斷類型,強制轉型,判斷參數符不符合指望值邊界等。java
因此,我就考慮了,實體類能夠經過Spring MVC
中Hibernate
的Validation
使用註解的方式進行參數校驗,那麼,少了實體類,我是否是能夠經過配置XML的方式來達到相似有實體類的效果。網上找了相似關鍵詞的工具類,發現沒有我所指望的,因此就動手來了一個。git
在Web
開發時,有許多if-else
語句的出現都是在爲了驗證前端參數合不合法真的是挺無奈的,並且有些代碼雖然長起來相似可是呢要去重構成一個公用的方法好像有些困難,時常問本身,要怎麼去搞,Java
不是JavaScript
,語句沒那麼靈活。github
因而想着經過XML
配置試試,大體就是經過配置好的XML
代替咱們的實體類,而且有個入口將XML
中的實體映射,並傳入待驗證的Map
,驗證以後傳出一個數組,若是驗證經過數組爲空,不經過則是咱們XML
中配置的對應錯誤語句。數組
動手在這以前,須要想好咱們大體的XML
結構是怎麼樣的。這裏,個人想法是,在咱們通常遇到的參數主要就是Integer
,String
,Double
,Date
,List
了(這裏竟然沒有考慮Boolean
,算了,以後再作補充也行)。因此基於以上,設計的結構大體以下:框架
<?xml version="1.0" encoding="UTF-8"?> <map-verify xmlns="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
,這裏就不貼出代碼了。less
以上的XML
大體的說了下怎麼使用其替代實體類。寫到了這裏其實有時還以爲使用實體類可能更方便,何須使用Map
呢?可是,別人框架已經寫了,參數只能接收到Map
那我只能屈服了。dom
首先,每個Entity
就是一個實體對象,這裏我認爲每一個Entity都應該包含有一個驗證方法和一個初始化方法,由於在進行XML
解析時就調用init
,在進行Map
驗證時就調用verify
方法這樣,那些String
節點也是相似的,解析初始化時就把XML
中配置的信息保存起來,等到驗證時就經過以前保存的信息進行判斷便可,沒必要從新解析了。ide
這裏以解析一個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; @Override public void init(Element currentEle, EntityFactory factory) { // 開始解析當前節點<Integer/> String name = currentEle.attributeValue("name"); // 獲取節點屬性castErrMsg,看是否存在castErrMsg this.castErrMsg = currentEle.attributeValue("castErrMsg"); if (Util.isEmpty(this.castErrMsg)) { this.castErrMsg = name + ": this is not integer type."; } else { this.castErrMsg = name + ": " + this.castErrMsg; } // 獲取子節點notNull Element 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) { @Override Integer valueOf(String value) { try { return Integer.valueOf(value); }catch (NumberFormatException e) { throw new ConvertException(castErrMsg); } } @Override boolean lessThan(Integer value, Integer lt) { return value < lt; } @Override boolean greaterThan(Integer value, Integer gt) { return value > gt; } @Override boolean lessThanOrEquals(Integer value, Integer lte) { return value <= lte; } @Override boolean greaterThanOrEquals(Integer value, Integer gte) { return value <= gte; } @Override boolean equals(Integer value, Integer eq) { return value.equals(eq); } }; } @Override public String[] verify(Object value) { //正式驗證參數是否合法 if (value == null) { //若是以前解析包含notNull,則這裏爲true,那麼將返回解析的notNullErrMsg if (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}; } } @Override public 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的判斷沒有實現,若是有須要就須要本身動手了:-)
)。之後也但願本身若是有什麼小想法儘可能的抽時間去作,不論作的好很差。