<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
雖然在Spring Boot中已經提供了非常多的預置註解,用以解決在日常開發工作中的各類內容,但是在特定情況仍然存在某些場景,無法滿足需求,需要自行定義相關的validator。本節將針對自定義的validator進行介紹。
自定義的註解
這裡的場景設定為進行IP地址的驗證,通過註解的方式,讓使用者使用驗證規則。註解定義如下:
@Target({ElementType.FIELD}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = IPAddressValidator.class) public @interface IPAddress { String message() default "{ipaddress.invalid}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
這個註解是作用在Field欄位上,執行時生效,觸發的是IPAddressValidator這個驗證類。
message
groups
payload
然後自定義Validator,這個是真正進行驗證的邏輯程式碼:
public class IPAddressValidator implements ConstraintValidator<IPAddress, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { Pattern pattern = compile("^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$"); Matcher matcher = pattern.matcher(value); try { if (!matcher.matches()) { return false; } else { for (int i = 1; i <= 4; i++) { int octet = Integer.valueOf(matcher.group(i)); if (octet > 255) { return false; } } return true; } } catch (Exception e) { return false; } } }
關於IP地址的驗證規則是通用的,具體邏輯不用太在意,主要是需要這裡Validator這個介面,以及其中的兩個泛型引數,第一個為註解名稱,第二個為實際欄位的資料型別。
定義了實體類CustomFieldBean.java
@Data public class CustomFieldBean { @IPAddress private String ipAddr; }
使用方法非常簡約,基於註解,無侵入邏輯。
單元測試用例
測試程式碼:
@RunWith(SpringRunner.class) @SpringBootTest public class CustomFieldValidatorTest { @Autowired private ProductService productService; @Test(expected = ConstraintViolationException.class) public void testInvalid() { CustomFieldBean customFieldBean = new CustomFieldBean(); customFieldBean.setIpAddr("1.2.33"); this.productService.doCustomField(customFieldBean); } @Test public void testValid() { CustomFieldBean customFieldBean = new CustomFieldBean(); customFieldBean.setIpAddr("1.2.33.123"); this.productService.doCustomField(customFieldBean); } }
如果不希望由系統自行觸發Validator的驗證邏輯,則可以由開發者自行進行驗證。在Spring Boot已經內建了Validator範例,直接將其載入進來即可。
使用範例如下:
@Autowired private Validator validator;
自定義執行的單元測試
測試程式碼如下:
@RunWith(SpringRunner.class) @SpringBootTest public class CodeValidationTest { @Autowired private Validator validator; @Test(expected = ConstraintViolationException.class) public void testValidator() { CustomFieldBean input = new CustomFieldBean(); input.setIpAddr("123.3.1"); Set<ConstraintViolation<CustomFieldBean>> violations = validator.validate(input); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } }
最近新開了一個專案,雖然hibernate-validator很好用,但是有時不能滿足稍微複雜一些的業務校驗。為了不在業務程式碼中寫校驗邏輯,以及讓程式碼更優雅,故而採用了自定義校驗註解的方式。
本例註解應用場景: 填寫表單時,某一項資料存在時,對應的一類資料都應存在,一同提交。
1.類註解
主註解用於標記要在校驗的實體類
@Target( { TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = RelateOtherValidator.class) @Documented public @interface RelateOther { String message() default ""; /** * 校驗數量 */ int num() default 2; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
2.輔助註解
輔助註解用於標註於要校驗的欄位,isMaster區分為主註解和從註解。
主註解是關鍵欄位,存在才進行校驗從註解對應欄位的有效性;主註解的value()屬性可以設定預設值,當欄位對應值對應value()時才開啟校驗。
從註解為等待校驗的值,預設為從註解。
@Target( { FIELD }) @Retention(RUNTIME) @Documented public @interface RelateOtherItem { /** * 是否為主欄位,主欄位存在才進行校驗 */ boolean isMaster() default false; /** * 用於開啟對指定值校驗判斷,master欄位有效 * 當前為master且value與標註欄位值相等才進行校驗, */ String value() default ""; }
3.校驗類
校驗類為實際執行校驗邏輯的類,在類註解的@Constraint的validatedBy屬性上設定。
要設定為校驗類,首先要實現ConstraintValidator類的isValid方法。
@Slf4j // @Slf4j是lombok的註解 public class RelateOtherValidator implements ConstraintValidator<RelateOther, Object> { // 要校驗的個數 private int validateNum; @Override public void initialize(RelateOther constraintAnnotation) { validateNum = constraintAnnotation.num(); } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { if (o == null) { return true; } Field[] declaredFields = o.getClass().getDeclaredFields(); boolean mater = false; int emptyNum = 0; try { // 總共需要校驗的欄位數 int totalValidateNum = validateNum; for (Field field : declaredFields) { // 校驗是否進行過標註 if (!field.isAnnotationPresent(RelateOtherItem.class)) { continue; } if (validateNum > 0 && totalValidateNum-- < 0) { return false; } field.setAccessible(true); Object property = field.get(o); RelateOtherItem relateOtherItem = field.getAnnotation(RelateOtherItem.class); // 主欄位不存在,則校驗通過 if (relateOtherItem.isMaster()) { if (property==null) { return true; } // 與指定值不一致,校驗通過 if (!StringUtils.isEmpty(relateOtherItem.value()) && !relateOtherItem.value().equals(property)) { return true; } mater = true; continue; } if (null == property) { emptyNum++; } } // 主欄位不存在,則校驗通過 if (!mater) { log.info("RelateOther註解主欄位不存在"); return true; } return emptyNum==0; } catch (Exception e) { log.info("RelateOther註解,解析異常 {}", e.getMessage()); return false; } } }
4.校驗失敗
註解校驗不同時會丟擲一個MethodArgumentNotValidException異常。這裡可以採用全域性例外處理的方法,進行捕獲處理。捕獲之後的異常可以獲取BindingResult 物件,後面就跟hibernate-validator處理方式一致了。
BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
5.使用demo
註解的使用類似下面,首先在請求實體類上標註類註解,再在對應的欄位上標註輔助註解。
@RelateOther(message = "xx必須存在!",num=2) public class MarkReq { @RelateOtherItem (isMaster= true,value="1") private Integer girl; @RelateOtherItem private Integer sunscreen; private String remarks; }
自定義註解在開發中還是很好用的,本文主要起到拋磚引玉的作用。對於首次使用的朋友應該還是有些用處的。
這些僅為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45