最近端午很久沒有和二胖聚一聚了,因而約了二胖到人民廣場去宰他一頓,正好最近他跳槽加薪了。<br/>
我:二胖據說你最近跳槽了,而且仍是從傳統軟件公司跳到了互聯網公司,工資是否是漲了一點啊,今天你請客哈。<br/>
二胖:別說了,工資是漲了點,可是性價比反而變低了,之前到點就下班,如今下班到家都快12點了。<br/>
我:新公司怎麼樣還適應嗎?除了上班時間久點。<br/>
二胖:哎,這個還真稍微有點不適應,這不是剛進去沒啥事,leader
就給我安排了一個簡單的用戶保存功能(參數校驗),原來之前公司個把小時就作好了的功能,在這新公司硬是折騰了兩三天,真是苦不堪言。我改了好幾個版本最終leader
才滿意的點了點頭。html
review
了(二胖在之前的公司代碼review
是不存在的,只要功能實現就行了)。正好leader
今天有點時間,看到新同事提交的代碼看看寫的怎麼樣。 看着這個裸奔的接口,leader
把二胖叫了過去,語重心長的跟二胖說道:"你這個參數校驗不寫寫嗎?不怕人家攻擊你的接口嗎?這裏不校驗,直接用,不怕引入sql注入嗎?這裏不校驗下郵箱是否符合格式嗎?這個判空也不寫,不怕大量的空指針,服務熔斷嗎?..."。面對leader
的拼命十三問,二胖心想試用期怕是有點難過哦?只能低着頭回到工位從新按照leader
的教育整改起來,而後又從新提交了。leader
看了看說到:「此次代碼比上次好多了,功能基本沒啥問題了,可是這一塊代碼是否是能夠在優化下,這樣寫不是很優雅」前端
if(Objects.isNull(user)){ throw new IllegalArgumentException("用戶不能爲空"); } if(StringUtils.isEmpty(user.getUserName())){ throw new IllegalArgumentException("用戶名不能爲空"); } if(StringUtils.isEmpty(user.getUserName())){ throw new IllegalArgumentException("用戶名不能爲空"); } if(StringUtils.isEmpty(user.getSex())){ throw new IllegalArgumentException("用戶性別不能爲空"); } if(Objects.isNull(user.getUserDetail())){ throw new IllegalArgumentException("用戶詳細信息不能爲空"); } if(Objects.isNull(user.getUserDetail().getAddress())){ throw new IllegalArgumentException("用戶地址不能爲空"); } if(!"M".equals(user.getSex()) && !"F".equals(user.getSex())){ throw new IllegalArgumentException("用戶性別不合法"); }
二胖也是一陣鬱悶,仍是懷念之前的公司啊,功能實現就好,代碼想怎麼寫就怎麼寫。互聯網公司就是規矩多,寫完代碼還要寫單測,還要監控一堆破事,活該這羣人996.時間都花到這上面去了。抱怨該抱怨可是代碼還得改啊。如今疫情期間好不容易找一個工做不能丟啊。
二狗想到之前不是學過aop
嗎?再配合下自定義註解,這樣代碼就應該比較優雅了吧,說幹就幹。java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.METHOD}) public @interface ParameterValidator { }
ParameterValidator
註解的方法。而後經過切面獲取全部請求的參數,獲取參數以後就解析參數上面的註解。配置切面啥的都比較簡單,稍微複雜的就是反射解析參數了,由於要涉及到請求參數的嵌套結構。二胖習慣性的面向百度編程能copy
別人的代碼堅定不去本身寫。百度出來的基本上都是單層結構,簡單基本類型的對象,沒有涉及到是嵌套、級聯的類型的情趣參數。最後在github
(全球最大的同性交友網站)找了一圈也沒有找到合適的。既然拿來主義沒有結果那就只能哼次哼次的本身寫了,幸虧本身之前學過點反射的知識。花了一個小時經過遞歸調用寫了個粗糙的版本,比較粗糙還有不少場景沒有考慮進去。不過基本能夠知足條件了部分代碼以下:git
public static void checkField(Object object, Class<?> aClass) throws IllegalAccessException { boolean primitive = isPrimitive(aClass); if (primitive) { return; } Field[] declaredFields = filterField(aClass.getDeclaredFields()); for (Field field : declaredFields) { makeAccessible(field); // 校驗咱們自定義註解 MyNotBlank fieldAnnotation = field.getAnnotation(MyNotBlank.class); Object currentObject = field.get(object); if (Objects.nonNull(fieldAnnotation)) { if (StringUtils.isEmpty(currentObject)) { throw new IllegalArgumentException(field.getName()+":"+fieldAnnotation.message()); } } if (!isJavaClass(field.getType())) { // 遞歸調用,有級聯參數時候 checkField(currentObject); } else if (field.getType().isPrimitive()) { } else if (field.getType().isAssignableFrom(List.class)) { // 遞歸調用,解析list類型 getList(field, currentObject); } } }
而後趕忙測試一波,還不錯基本功能實現了,可以實現判空檢驗了,也能夠實現級聯校驗了。效果以下:
不過這個如今支持類型爲基本類型和String
、List
的
後續若是參數類型是數組、或者Map
等等還得去解析。 這時候同事二狗從旁邊走過,看到二胖這麼認真的在敲代碼。<br/>
二狗:二胖你又在寫什麼bug
啊。<br/>
二胖:在本身造個輪子,寫個通用的參數校驗。<br/>
二狗:這個如今市面上不是已經有現成的方案了嗎?jsr(Java Specification Requests)
能夠去了解下哦。<br/>
二胖:好的我立刻去查下資料。github
jsr
咱們就得先了解下什麼是JCP(Java Community Process)
?JCP(Java Community Process) 是一個開放的國際組織,主要由Java開發者以及被受權者組成,職能是發展和更新。
它是指向JCP提出新增一個標準化技術規範的正式請求。任何人均可以提交JSR,(若是你以爲本身牛逼你也能夠提交一個)
以向Java平臺增添新的API和服務。JSR已成爲Java界的一個重要標準。
Bean Validation 顧名思義是對 java Bean 的校驗,目前爲止,Java 對 Bean 的校驗有3個規範。web
Hibernate Validator
是 Bean Validation
的參考實現 . Hibernate Validator
提供了 JSR 303
規範中全部內置 constraint 的實現,除此以外還有一些附加的 constraint
。正則表達式
2.3
之後的版本 spring-boot-starter-web
已經去除了這個依賴,須要手動引入 Hibernate-validator
依賴,詳細內容見官網描述 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
springboot
項目的話直接引入<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.17.Final</version> </dependency>
代碼演示:
方法前面這個註解@Valid
是必須的,不然不生效哦。spring
@PostMapping(value = "/save2") @ResponseBody public ResultViewModel save2(@Valid @RequestBody User user){ boolean saveUser = saveUser(user); if (saveUser) { return ResultViewModelUtil.success(); } return ResultViewModelUtil.error(); }
實體類上標上須要校驗的規則註解就行了。sql
//被註釋的元素,值必須是一個字符串,不能爲null,且調用trim()後,長度必須大於0 @NotBlank(message = "") //被註釋的元素,值不能爲null,但能夠爲"空",用於基本數據類型的非空校驗上,並且被其標註的字段可使用 @size、@Max、@Min 等對字段數值進行大小的控制 @NotNull(message = "") //被註釋的的元素,值不能爲null,且長度必須大於0,通常用在集合類上面 @NotEmpty(message = "") //被註釋的元素必須符合指定的正則表達式。 @Pattern(regexp = "", message = "") //被註釋的元素的大小必須在指定的範圍內。 @Size(min =, max =) //被註釋的元素,值必須是一個數字,且值必須大於等於指定的最小值 @Min(value = long之內的值, message = "") //被註釋的元素,值必須是一個數字,且值必須小於等於指定的最大值 @Max(value = long之內的值, message = "") //被註釋的元素,值必須是一個數字,其值必須大於等於指定的最小值 @DecimalMin(value = 能夠是小數, message = "") //被註釋的元素,值必須是一個數字,其值必須小於等於指定的最大值 @DecimalMax(value = 能夠是小數, message = "") //被註釋的元素,值必須爲null @Null(message = "") //被註釋的元素必須是一個數字,其值必須在可接受的範圍內 @Digits(integer =, fraction =) //被註釋的元素,值必須爲true @AssertTrue(message = "") //被註釋的元素,值必須爲false @AssertFalse(message = "") //被註釋的元素必須是一個過去的日期 @Past(message = "") //被註釋的元素必須是一個未來的日期 @Future(message = "") //被註釋的元素必須是電子郵件地址 @Email(regexp = "", message = "") //被註釋的元素必須在合適的範圍內 @Range(min =, max =, message = "") //被註釋的字符串的大小必須在指定的範圍內 @Length(min =, max =, message = "")
惟一須要注意的點就是若是是級聯校驗的話須要在最外層加上@Valid
爲何須要在校驗的上一次標上@Valid
這個註解,裏面的校驗纔會生效列?有知道的或者感興趣的能夠去看看源碼給我留言哦。
而後在配置一個全局的異常捕獲器就行了,因爲篇幅緣由代碼就不貼了,代碼上傳到了github
上。
校驗結果:編程
Hibernate-Validator
還能夠自定義註解實現。userId
(由於系統生成);修改的時候須要驗證userId
,這時候可用用戶到validator
的分組驗證功能)springboot
的、好比使用的是Jfinal框架(這個是個國產框架大多數人能都不知道)、或者soa
調用參數校驗的時候,這時候能夠怎麼使用列?api
,以及炒雞簡單的用法,趕忙把本身寫的輪子給刪除了,立馬換上了這個Hibernate-Validator
框架。從新修改提交後,leader
的臉上終於露出了滿意的笑容。參考
http://docs.jboss.org/hiberna...(官網中文版本貼心吧
)
https://docs.jboss.org/hibern...
https://juejin.im/post/5dd8d4...
https://www.cnblogs.com/mr-ya...