亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Validator從零掌握對象校驗的詳細過程

 更新時間:2025年02月25日 16:06:35   作者:小巫程序Demo日記  
SpringValidator學習指南從零掌握對象校驗,涵蓋Validator接口、嵌套對象處理、錯誤代碼解析等核心概念,幫助開發(fā)者實現數據校驗的規(guī)范與高效,本文詳細介紹Spring Validator從零掌握對象校驗,感興趣的朋友一起看看吧

Spring Validator 學習指南:從零掌握對象校驗

一、Validator 接口的作用:你的數據“守門員”

想象你開發(fā)了一個用戶注冊功能,用戶提交的數據可能有各種問題:名字沒填、年齡寫成了負數……這些錯誤如果直接保存到數據庫,會導致后續(xù)流程出錯。Validator 就像一位嚴格的守門員,在數據進入系統前,檢查每個字段是否符合規(guī)則。

核心任務

  • 檢查對象屬性是否合法(如非空、數值范圍)。
  • 收集錯誤信息,方便后續(xù)提示用戶。

二、Validator 接口的兩大方法:如何工作?

1. supports(Class clazz):我能處理這個對象嗎?

  • 作用:判斷當前 Validator 是否支持校驗某個類的對象。
  • 關鍵選擇
    • 精確匹配return Person.class.equals(clazz); → 只校驗 Person 類。
    • 靈活匹配return Person.class.isAssignableFrom(clazz); → 支持 Person 及其子類。

示例場景

  • 如果你有一個 Student extends Person,使用 equals 時,Student 對象不會被校驗;使用 isAssignableFrom 則會校驗。

2. validate(Object target, Errors errors):執(zhí)行校驗!

  • 作用:編寫具體的校驗規(guī)則,發(fā)現錯誤時記錄到 Errors 對象。
  • 常用工具ValidationUtils 簡化非空檢查。

示例代碼

public void validate(Object target, Errors errors) {
    // 檢查 name 是否為空
    ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
    Person person = (Person) target;
    // 檢查年齡是否合法
    if (person.getAge() < 0) {
        errors.rejectValue("age", "negative.age", "年齡不能為負數!");
    }
}

三、處理嵌套對象:如何避免重復代碼?

假設你有一個 Customer 類,包含 Address 對象:

public class Customer {
    private String firstName;
    private String surname;
    private Address address; // 嵌套對象
}

問題:直接在一個 Validator 中校驗所有字段

缺點

  • 若其他類(如 Order)也包含 Address,需重復編寫地址校驗代碼。
  • 維護困難:修改地址規(guī)則時,需改動多處代碼。

正確做法:

拆分 Validator,組合使用!

步驟 1:為每個類創(chuàng)建獨立的 Validator

  • AddressValidator(校驗地址):
public class AddressValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Address.class.isAssignableFrom(clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "street", "street.empty");
        ValidationUtils.rejectIfEmpty(errors, "city", "city.empty");
    }
}

CustomerValidator(校驗客戶,并復用 AddressValidator):

public class CustomerValidator implements Validator {
    private final Validator addressValidator;
    // 通過構造函數注入 AddressValidator
    public CustomerValidator(Validator addressValidator) {
        this.addressValidator = addressValidator;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        // 1. 校驗客戶的直屬字段(firstName, surname)
        ValidationUtils.rejectIfEmpty(errors, "firstName", "firstName.empty");
        ValidationUtils.rejectIfEmpty(errors, "surname", "surname.empty");
        Customer customer = (Customer) target;
        Address address = customer.getAddress();
        // 2. 校驗嵌套的 Address 對象
        if (address == null) {
            errors.rejectValue("address", "address.null");
            return;
        }
        // 3. 關鍵:切換錯誤路徑到 "address",防止字段名沖突
        errors.pushNestedPath("address");
        try {
            ValidationUtils.invokeValidator(addressValidator, address, errors);
        } finally {
            errors.popNestedPath(); // 恢復原始路徑
        }
    }
}

步驟 2:實際使用

// 創(chuàng)建 Validator
AddressValidator addressValidator = new AddressValidator();
CustomerValidator customerValidator = new CustomerValidator(addressValidator);
// 準備測試數據
Customer customer = new Customer();
customer.setFirstName(""); // 空名字
customer.setAddress(new Address()); // 空地址
// 執(zhí)行校驗
Errors errors = new BeanPropertyBindingResult(customer, "customer");
customerValidator.validate(customer, errors);
// 輸出錯誤
if (errors.hasErrors()) {
    errors.getAllErrors().forEach(error -> {
        System.out.println("字段:" + error.getObjectName() + "." + error.getCode());
    });
}
// 輸出結果:
// 字段:customer.firstName.empty
// 字段:customer.address.street.empty

四、關鍵技巧與常見問題

1. 錯誤路徑管理

pushNestedPathpopNestedPath
確保嵌套對象的錯誤字段帶上前綴(如 address.street),避免與主對象的字段名沖突。

2. 防御性編程

在組合 Validator 時,檢查注入的 Validator 是否支持目標類型:

public CustomerValidator(Validator addressValidator) {
    if (!addressValidator.supports(Address.class)) {
        throw new IllegalArgumentException("必須支持 Address 類型!");
    }
    this.addressValidator = addressValidator;
}

3. 國際化支持

錯誤代碼(如 firstName.empty)可對應語言資源文件(如 messages_zh.properties),實現多語言提示:

# messages_zh.properties
firstName.empty=名字不能為空
address.street.empty=街道地址不能為空

五、總結:為什么這樣設計?

  • 代碼復用AddressValidator 可被其他需要校驗地址的類(如 Order、Company)直接使用。
  • 單一職責:每個 Validator 只負責一個類的校驗,邏輯清晰,易于維護。
  • 靈活擴展:新增嵌套對象(如 PaymentInfo)時,只需創(chuàng)建新的 Validator 并注入,無需修改已有代碼。

3.2. 將錯誤代碼解析為錯誤信息:深入解析與實例演示

一、核心概念:錯誤代碼的多層次解析

當你在 Spring 中調用 rejectValue 方法注冊錯誤時(例如校驗用戶年齡不合法),Spring 不會只記錄你指定的單一錯誤代碼,而是自動生成一組層級化的錯誤代碼。這種設計允許開發(fā)者通過不同層級的錯誤代碼,靈活定義錯誤消息,實現“從具體到通用”的覆蓋策略。

二、錯誤代碼生成規(guī)則

假設在 PersonValidator 中觸發(fā)以下校驗邏輯:

errors.rejectValue("age", "too.darn.old");

生成的錯誤代碼(按優(yōu)先級從高到低):

  • too.darn.old.age.int字段名 + 錯誤代碼 + 字段類型
  • too.darn.old.age字段名 + 錯誤代碼
  • too.darn.old原始錯誤代碼

三、消息資源文件的匹配策略

Spring 的 MessageSource 會按照錯誤代碼的優(yōu)先級順序,在消息資源文件(如 messages.properties)中查找對應的消息。一旦找到匹配項,立即停止搜索。

示例消息資源文件

# messages.properties
too.darn.old.age.int=年齡必須是整數且不超過 120 歲
too.darn.old.age=年齡不能超過 120 歲
too.darn.old=輸入的值不合理

匹配過程

  • 優(yōu)先查找 too.darn.old.age.int → 若存在則使用。
  • 若未找到,查找 too.darn.old.age → 若存在則使用。
  • 最后查找 too.darn.old → 作為兜底消息。

四、實戰(zhàn)演示:從代碼到錯誤消息

步驟 1:創(chuàng)建實體類與校驗器

// Person.java
public class Person {
    private String name;
    private int age;
    // getters/setters
}
// PersonValidator.java
public class PersonValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Person.class.isAssignableFrom(clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        Person person = (Person) target;
        if (person.getAge() > 120) {
            errors.rejectValue("age", "too.darn.old");
        }
    }
}

步驟 2:配置消息資源文件

src/main/resources/messages.properties 中定義:

# 具體到字段類型
too.darn.old.age.int=年齡必須是整數且不能超過 120 歲
# 具體到字段
too.darn.old.age=年齡不能超過 120 歲
# 通用錯誤
too.darn.old=輸入的值無效

步驟 3:編寫測試代碼

@SpringBootTest
public class ValidationTest {
    @Autowired
    private MessageSource messageSource;
    @Test
    public void testAgeValidation() {
        Person person = new Person();
        person.setAge(150); // 觸發(fā)錯誤
        Errors errors = new BeanPropertyBindingResult(person, "person");
        PersonValidator validator = new PersonValidator();
        validator.validate(person, errors);
        // 提取錯誤消息
        errors.getFieldErrors("age").forEach(error -> {
            String message = messageSource.getMessage(error.getCode(), null, Locale.getDefault());
            System.out.println("錯誤消息:" + message);
        });
    }
}

輸出結果

錯誤消息:年齡必須是整數且不能超過 120 歲

解析
因為 too.darn.old.age.int 在消息文件中存在,優(yōu)先使用該消息。若刪除這行,則會匹配 too.darn.old.age,以此類推。

五、自定義錯誤代碼生成策略

默認的 DefaultMessageCodesResolver 生成的代碼格式為:
錯誤代碼 + 字段名 + 字段類型。
若需修改規(guī)則,可自定義 MessageCodesResolver。

示例:簡化錯誤代碼

@Configuration
public class ValidationConfig {
    @Bean
    public MessageCodesResolver messageCodesResolver() {
        DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
        resolver.setMessageCodeFormatter(DefaultMessageCodesResolver.Format.POSTFIX_ERROR_CODE);
        return resolver;
    }
}

效果
調用 rejectValue("age", "too.darn.old") 生成的代碼變?yōu)椋?/p>

  • age.too.darn.old
  • too.darn.old

六、常見問題與解決方案

問題 1:如何查看實際生成的錯誤代碼?

在測試代碼中打印錯誤對象:

errors.getFieldErrors("age").forEach(error -> {
    System.out.println("錯誤代碼列表:" + Arrays.toString(error.getCodes()));
});

輸出

錯誤代碼列表:[too.darn.old.age.int, too.darn.old.age, too.darn.old]

問題 2:字段類型在代碼中如何表示?

Spring 使用字段的簡單類名(如 int、String)。對于自定義類型(如 Address),代碼中會使用 address(類名小寫)。

七、總結:為何需要層級化錯誤代碼?

  • 靈活覆蓋:允許針對特定字段或類型定制消息,同時提供通用兜底。
  • 國際化友好:不同語言可定義不同層級的消息,無需修改代碼。
  • 代碼解耦:校驗邏輯與具體錯誤消息分離,提高可維護性。

學習建議

  • 通過調試觀察 errors.getCodes() 的輸出,深入理解代碼生成規(guī)則。
  • 在項目中優(yōu)先使用字段級錯誤代碼(如 too.darn.old.age),提高錯誤消息的精準度。

到此這篇關于Spring Validator 學習指南:從零掌握對象校驗的文章就介紹到這了,更多相關Spring Validator 對象校驗內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java基礎必學TreeSet集合

    Java基礎必學TreeSet集合

    這篇文章主要介紹了Java必學基礎TreeSet集合,TreeSet集合實現了SortedSet接口,?可以對集合中元素進行自然排序,?要求集合中的元素必須是可比較的。下文詳細介紹需要的朋友可以參考一下
    2022-04-04
  • MyEclipse安裝JS代碼提示的教程(Spket插件)

    MyEclipse安裝JS代碼提示的教程(Spket插件)

    本篇文章主要介紹了MyEclipse安裝JS代碼提示的教程(Spket插件),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • maven打包zip包含bin下啟動腳本的完整代碼

    maven打包zip包含bin下啟動腳本的完整代碼

    這篇文章主要介紹了maven打包zip包含bin下啟動腳本,本文給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • SpringBoot中使用Guava實現單機令牌桶限流的示例

    SpringBoot中使用Guava實現單機令牌桶限流的示例

    本文主要介紹了SpringBoot中使用Guava實現單機令牌桶限流的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • 教你如何把Eclipse創(chuàng)建的Web項目(非Maven)導入Idea

    教你如何把Eclipse創(chuàng)建的Web項目(非Maven)導入Idea

    這篇文章主要介紹了教你如何把Eclipse創(chuàng)建的Web項目(非Maven)導入Idea,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • 關于jvm內存如何查看

    關于jvm內存如何查看

    本文介紹了如何在Java虛擬機(JVM)中進行內存管理,包括查看JVM內存情況的常用方法和工具,這些方法包括使用JDK自帶的工具如jps、jstat、jmap和jconsole,以及一些第三方監(jiān)控工具如Prometheus、Grafana和ElasticStack等,通過這些方法
    2024-09-09
  • 小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Java框架設計靈魂之反射的示例詳解

    Java框架設計靈魂之反射的示例詳解

    反射就是把Java類中的各個成員映射成一個個的Java對象。本文將通過示例為大家詳細講解Java框架設計的靈魂:反射,感興趣的可以了解一下
    2022-06-06
  • Spring.Net IOC依賴注入原理流程解析

    Spring.Net IOC依賴注入原理流程解析

    這篇文章主要介紹了Spring.Net IOC依賴注入原理流程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • 淺談Java并發(fā)中ReentrantLock鎖應該怎么用

    淺談Java并發(fā)中ReentrantLock鎖應該怎么用

    本文主要介紹了ava并發(fā)中ReentrantLock鎖的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11

最新評論