知識庫 / Spring / Spring Web RSS 訂閱

Spring 中 @Valid 和 @Validated 註解的區別

Spring Web
HongKong
5
12:45 PM · Dec 06 ,2025

1. 概述

本快速教程將重點介紹 Spring 中的 @Valid@Validated 註解之間的差異。

驗證用户輸入是大多數應用程序中常見的功能。在 Java 生態系統中,我們專門使用 Java 標準 Bean 驗證 API 來支持此功能,該 API 從 Spring 4.0 版本開始與 Spring 深度集成。 @Valid 和 @Validated 註解源於該標準 Bean API。

在下一部分,我們將更詳細地探討它們。

2. @Valid@Validated 註解

在 Spring 中,我們使用 JSR-303 的 @Valid 註解用於方法級別的驗證。我們還將其用於標記成員屬性的驗證。 但是,此註解不支持組級別的驗證。

組有助於限制驗證過程中應用的約束。一個典型的用例是 UI 流程嚮導。在第一步中,我們可能只有一組子屬性。在後續步驟中,可能還有另一個屬於同一 bean 的組。因此,我們需要在每個步驟中對這些有限的字段應用約束,但 @Valid 不支持此功能。

在這種情況下,對於組級別的驗證,我們必須使用 Spring 的 @Validated 它是 JSR-303 的 @Valid 的變體。此註解主要用於方法級別。 對於標記成員屬性,我們仍然使用 @Valid 註解。

現在,讓我們直接來看一下這些註解的用法,並提供一個示例。

@Valid
public class User {

    @Validated
    private String name;

    @Validated
    private int age;
}

3. 示例

讓我們考慮一個使用 Spring Boot 開發的簡單用户註冊表單。首先,我們將僅包含 姓名密碼 屬性:

public class UserAccount {

    @NotNull
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank
    private String name;

    // standard constructors / setters / getters / toString
     
}

接下來,我們來看一下控制器。在這裏,我們將有 saveBasicInfo方法,帶有 @Valid註解,用於驗證用户輸入:

@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());
}

在確認測試運行成功後,我們將擴展功能。接下來最自然的步驟是將其轉換為多步驟註冊表單,就像大多數嚮導所採用的。第一步,包括姓名密碼,保持不變。在第二步,我們將獲取額外信息,例如年齡電話。然後我們將使用這些額外字段更新我們的領域對象:

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</em/> 裝飾器來註解該屬性:

public class UserAccount {
    
    //...
    
    @Valid
    @NotNull(groups = AdvanceInfo.class)
    private UserAddress useraddress;
    
    // standard constructors / setters / getters / toString 
}

5. 優缺點

讓我們來看一下在 Spring 中使用 <em @Valid</em><em @Validated</em> 註解的優缺點。

@Valid 註解確保對整個對象的驗證。 重要的是,它執行整個對象圖的驗證。 但是,這在需要僅部分驗證的場景中會產生問題。

另一方面,我們可以使用 @Validated 進行組驗證,包括上述部分驗證。 但是,在這種情況下,被驗證的實體必須知道它們所使用的所有組或用例的驗證規則,或者混合了關注點,這可能會導致反模式。

6. 結論

在本文中,我們探討了 @Valid@Validated 註解之間的關鍵差異。

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

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.