Spring中@Valid和@Validated註解的差異

1.概述

在本快速教程中,我們將重點介紹Spring中@Valid@Validated註解之間的區別。

驗證用戶輸入是我們大多數應用程序中的常見功能。在Java生態系統中,我們專門使用Java標準Bean驗證API來支持此功能。而且,從4.0版本開始,它也與Spring很好地集成在一起。 @Valid@Validated註解源自此Standard Bean API

在下一節中,我們將詳細介紹它們。

2. @Valid@Validated註解

在Spring中,我們使用JSR-303的**@Valid批註進行方法級別驗證此外,我們還使用它來標記成員屬性以進行驗證**。但是,此註釋不支持組驗證。

組有助於限制驗證期間應用的約束。 UI嚮導是一種特殊的用例。在這裡,第一步,我們可能有一個特定的字段子組。在後續步驟中,可能存在另一個屬於同一bean的組。因此,我們需要在每個步驟中對這些有限的字段應用約束,但是@Valid不支持此約束。

在這種情況下,**對於組級別,我們必須使用Spring的@Validated,**這是此JSR-303的@Valid的變體。在方法級別使用。為了標記成員屬性,我們繼續使用@Valid批註。

現在,讓我們深入研究一下,並通過示例查看這些註釋的用法。

3.例子

讓我們考慮一個使用Spring Boot開發的簡單用戶註冊表單。首先,我們只有namepassword屬性:

public class UserAccount {



 @NotNull

 @Size(min = 4, max = 15)

 private String password;



 @NotBlank

 private String name;



 // standard constructors / setters / getters / toString



 }

接下來,讓我們看一下控制器。在這裡,我們將使用帶有@Valid批註的saveBasicInfo方法來驗證用戶輸入:

@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST)

 public String saveBasicInfo(

 @Valid @ModelAttribute("useraccount") UserAccount useraccount,

 BindingResult result,

 ModelMap model) {

 if (result.hasErrors()) {

 return "error";

 }

 return "success";

 }

現在讓我們測試一下這個方法:

@Test

 public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception {

 this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo")

 .accept(MediaType.TEXT_HTML)

 .param("name", "test123")

 .param("password", "pass"))

 .andExpect(view().name("success"))

 .andExpect(status().isOk())

 .andDo(print());

 }

在確認測試成功運行之後,現在讓我們擴展功能。下一步的邏輯步驟是將其轉換為多步驟註冊表格,就像大多數嚮導一樣。第一步, namepassword保持不變。在第二步中,我們將獲取其他信息,例如agephone 。因此,我們將使用以下其他字段更新域對象:

public class UserAccount {



 @NotNull

 @Size(min = 4, max = 15)

 private String password;



 @NotBlank

 private String name;



 @Min(value = 18, message = "Age should not be less than 18")

 private int age;



 @NotBlank

 private String phone;



 // standard constructors / setters / getters / toString



 }

但是,這一次,我們將注意到先前的測試失敗。這是因為我們沒有傳遞agephone字段,這些字段仍然不在UI的圖片中.為了支持此行為,我們將需要組驗證和@Validated批註。

為此,我們需要對字段進行分組以創建兩個不同的組。首先,我們需要創建兩個標記接口。每個組或每個步驟都有一個單獨的名稱。我們可以參考我們關於組驗證的文章來實現此目的。在這裡,讓我們關註註釋中的差異。

第一步將具有BasicInfo接口,第二步將具有AdvanceInfo 。此外,我們將更新UserAccount類以使用這些標記接口,如下所示:

public class UserAccount {



 @NotNull(groups = BasicInfo.class)

 @Size(min = 4, max = 15, groups = BasicInfo.class)

 private String password;



 @NotBlank(groups = BasicInfo.class)

 private String name;



 @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class)

 private int age;



 @NotBlank(groups = AdvanceInfo.class)

 private String phone;



 // standard constructors / setters / getters / toString



 }

另外,我們現在將更新控制器以使用@Validated註釋而不是@Valid

@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST)

 public String saveBasicInfoStep1(

 @Validated(BasicInfo.class)

 @ModelAttribute("useraccount") UserAccount useraccount,

 BindingResult result, ModelMap model) {

 if (result.hasErrors()) {

 return "error";

 }

 return "success";

 }

作為此更新的結果,我們的測試現在可以成功運行。現在,我們還要測試這個新方法:

@Test

 public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception {

 this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1")

 .accept(MediaType.TEXT_HTML)

 .param("name", "test123")

 .param("password", "pass"))

 .andExpect(view().name("success"))

 .andExpect(status().isOk())

 .andDo(print());

 }

這也成功運行。因此,我們可以看到**@Validated的用法對於組驗證至關重要。**

接下來,讓我們看看@Valid對於觸發嵌套屬性驗證是必不可少的。

4.使用@Valid批註標記嵌套對象

@Valid批註特別用於標記嵌套屬性。這觸發了嵌套對象的驗證。例如,在我們當前的場景中,讓我們創建一個UserAddress對象:

public class UserAddress {



 @NotBlank

 private String countryCode;



 // standard constructors / setters / getters / toString

 }

為了確保驗證此嵌套對象,我們將使用@Valid批註裝飾屬性:

public class UserAccount {



 //...



 @Valid

 @NotNull(groups = AdvanceInfo.class)

 private UserAddress useraddress;



 // standard constructors / setters / getters / toString

 }

5.利與弊

讓我們看看在Spring中使用@Valid@Validated批註的一些優缺點。

@Valid批註確保整個對象的驗證。重要的是,它執行整個對像圖的驗證。但是,這為僅需要部分驗證的方案帶來了問題。

另一方面,我們可以使用@Validated進行組驗證,包括上面的部分驗證。但是,在這種情況下,經過驗證的實體必須知道其使用的所有組或用例的驗證規則。在這裡,我們混合了各種顧慮,因此這可能會導致產生反模式。

六,結論

在本快速教程中,我們探討了@Valid@Validated批註之間的主要區別。

總之,對於任何基本驗證,我們將在方法調用中使用JSR @Valid批註。另一方面,對於包括組序列在內的任何組驗證,我們都需要在方法調用中使用Spring的@Validated批註。還需要@Valid批註來觸發嵌套屬性的驗證。

與往常一樣,本文提供的代碼可從GitHub上獲得