欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringValidation數(shù)據(jù)校驗(yàn)之約束注解與分組校驗(yàn)方式

 更新時(shí)間:2025年04月15日 14:27:50   作者:程序媛學(xué)姐  
本文將深入探討Spring Validation的核心功能,幫助開(kāi)發(fā)者掌握約束注解的使用技巧和分組校驗(yàn)的高級(jí)應(yīng)用,從而構(gòu)建更加健壯和可維護(hù)的Java應(yīng)用程序,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

引言

數(shù)據(jù)校驗(yàn)是企業(yè)級(jí)應(yīng)用中的核心需求,它確保了業(yè)務(wù)數(shù)據(jù)的準(zhǔn)確性和一致性。

Spring Validation提供了一套強(qiáng)大而靈活的數(shù)據(jù)校驗(yàn)框架,通過(guò)聲明式的約束注解和分組校驗(yàn)機(jī)制,優(yōu)雅地實(shí)現(xiàn)了復(fù)雜的驗(yàn)證邏輯。

一、Spring Validation基礎(chǔ)架構(gòu)

1.1 JSR-380標(biāo)準(zhǔn)與Spring整合

Spring Validation以JSR-380(Bean Validation 2.0)為基礎(chǔ),通過(guò)與Hibernate Validator的無(wú)縫整合,提供了全面的數(shù)據(jù)校驗(yàn)解決方案。

JSR-380定義了標(biāo)準(zhǔn)的約束注解和驗(yàn)證API,Spring擴(kuò)展了這一標(biāo)準(zhǔn)并提供了更豐富的功能支持。

這種整合使開(kāi)發(fā)者能夠以聲明式方式定義校驗(yàn)規(guī)則,大大簡(jiǎn)化了數(shù)據(jù)驗(yàn)證的復(fù)雜性。

// Spring Validation依賴配置
/*
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
*/

// 啟用驗(yàn)證的基本配置
@Configuration
public class ValidationConfig {
    
    @Bean
    public Validator validator() {
        return Validation.buildDefaultValidatorFactory().getValidator();
    }
    
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

1.2 校驗(yàn)處理流程

Spring Validation的校驗(yàn)流程由多個(gè)核心組件協(xié)同完成。當(dāng)一個(gè)標(biāo)記了約束注解的對(duì)象被提交驗(yàn)證時(shí),ValidatorFactory創(chuàng)建Validator實(shí)例,然后遍歷對(duì)象的所有屬性,檢查是否滿足約束條件。

對(duì)于不滿足條件的屬性,會(huì)生成對(duì)應(yīng)的ConstraintViolation,包含違反信息和元數(shù)據(jù)。這些違反信息可以被收集并轉(zhuǎn)化為用戶友好的錯(cuò)誤消息。

// 手動(dòng)校驗(yàn)示例
@Service
public class ValidationService {
    
    @Autowired
    private Validator validator;
    
    public <T> ValidationResult validate(T object) {
        ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<T>> violations = validator.validate(object);
        
        if (!violations.isEmpty()) {
            result.setValid(false);
            
            Map<String, String> errorMap = violations.stream()
                .collect(Collectors.toMap(
                    v -> v.getPropertyPath().toString(),
                    ConstraintViolation::getMessage,
                    (msg1, msg2) -> msg1 + "; " + msg2
                ));
                
            result.setErrorMessages(errorMap);
        }
        
        return result;
    }
}

// 校驗(yàn)結(jié)果封裝
public class ValidationResult {
    private boolean valid = true;
    private Map<String, String> errorMessages = new HashMap<>();
    
    // Getters and setters
    
    public boolean hasErrors() {
        return !valid;
    }
}

二、約束注解詳解

2.1 常用內(nèi)置約束注解

Spring Validation提供了豐富的內(nèi)置約束注解,覆蓋了常見(jiàn)的校驗(yàn)場(chǎng)景。這些注解可以分為幾類:基本驗(yàn)證(如@NotNull、@NotEmpty)、數(shù)字驗(yàn)證(如@Min、@Max)、字符串驗(yàn)證(如@Size、@Pattern)和時(shí)間驗(yàn)證(如@Past、@Future)等。

每個(gè)注解都可以通過(guò)message屬性自定義錯(cuò)誤消息,提高用戶體驗(yàn)。此外,大多數(shù)注解還支持通過(guò)payload屬性關(guān)聯(lián)額外的元數(shù)據(jù)。

// 內(nèi)置約束注解使用示例
@Entity
public class Product {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @NotBlank(message = "產(chǎn)品名稱不能為空")
    @Size(min = 2, max = 50, message = "產(chǎn)品名稱長(zhǎng)度必須在2-50之間")
    private String name;
    
    @NotNull(message = "價(jià)格不能為空")
    @Positive(message = "價(jià)格必須是正數(shù)")
    @Digits(integer = 6, fraction = 2, message = "價(jià)格格式不正確")
    private BigDecimal price;
    
    @Min(value = 0, message = "庫(kù)存不能為負(fù)數(shù)")
    private Integer stock;
    
    @NotEmpty(message = "產(chǎn)品分類不能為空")
    private List<@NotBlank(message = "分類名稱不能為空") String> categories;
    
    @Pattern(regexp = "^[A-Z]{2}\\d{6}$", message = "產(chǎn)品編碼格式不正確,應(yīng)為2個(gè)大寫(xiě)字母+6位數(shù)字")
    private String productCode;
    
    @Email(message = "聯(lián)系郵箱格式不正確")
    private String contactEmail;
    
    @Past(message = "創(chuàng)建日期必須是過(guò)去的時(shí)間")
    private LocalDate createdDate;
    
    // Getters and setters
}

2.2 自定義約束注解

當(dāng)內(nèi)置約束無(wú)法滿足特定業(yè)務(wù)需求時(shí),自定義約束注解是一個(gè)強(qiáng)大的解決方案。創(chuàng)建自定義約束需要兩個(gè)核心組件:約束注解定義和約束驗(yàn)證器實(shí)現(xiàn)。注解定義聲明元數(shù)據(jù),如默認(rèn)錯(cuò)誤消息和應(yīng)用目標(biāo);驗(yàn)證器實(shí)現(xiàn)則包含實(shí)際的驗(yàn)證邏輯。通過(guò)組合現(xiàn)有約束或?qū)崿F(xiàn)全新邏輯,可以構(gòu)建出適合任何業(yè)務(wù)場(chǎng)景的驗(yàn)證規(guī)則。

// 自定義約束注解示例 - 中國(guó)手機(jī)號(hào)驗(yàn)證
@Documented
@Constraint(validatedBy = ChinesePhoneValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ChinesePhone {
    
    String message() default "手機(jī)號(hào)格式不正確";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
}

// 約束驗(yàn)證器實(shí)現(xiàn)
public class ChinesePhoneValidator implements ConstraintValidator<ChinesePhone, String> {
    
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public void initialize(ChinesePhone annotation) {
        // 初始化邏輯,如果需要
    }
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 如果需要非空校驗(yàn),應(yīng)該額外使用@NotNull
        }
        
        return PHONE_PATTERN.matcher(value).matches();
    }
}

// 使用自定義約束
public class User {
    
    @NotNull(message = "姓名不能為空")
    private String name;
    
    @ChinesePhone
    private String phoneNumber;
    
    // 其他字段和方法
}

三、分組校驗(yàn)深入應(yīng)用

3.1 分組校驗(yàn)基本原理

分組校驗(yàn)是Spring Validation的一個(gè)強(qiáng)大特性,允許根據(jù)不同的業(yè)務(wù)場(chǎng)景應(yīng)用不同的校驗(yàn)規(guī)則。通過(guò)定義接口作為分組標(biāo)識(shí),并在約束注解中指定所屬分組,可以實(shí)現(xiàn)精細(xì)化的驗(yàn)證控制。分組校驗(yàn)解決了一個(gè)實(shí)體類在不同操作(如新增、修改、刪除)中面臨的差異化驗(yàn)證需求,避免了代碼重復(fù)和維護(hù)困難。

// 分組校驗(yàn)的基本使用
// 定義驗(yàn)證分組
public interface Create {}
public interface Update {}
public interface Delete {}

// 使用分組約束
@Entity
public class Customer {
    
    @NotNull(groups = {Update.class, Delete.class}, message = "ID不能為空")
    @Null(groups = Create.class, message = "創(chuàng)建時(shí)不應(yīng)指定ID")
    private Long id;
    
    @NotBlank(groups = {Create.class, Update.class}, message = "名稱不能為空")
    private String name;
    
    @NotBlank(groups = Create.class, message = "創(chuàng)建時(shí)密碼不能為空")
    private String password;
    
    @Email(groups = {Create.class, Update.class}, message = "郵箱格式不正確")
    private String email;
    
    // Getters and setters
}

// 在控制器中使用分組校驗(yàn)
@RestController
@RequestMapping("/customers")
public class CustomerController {
    
    @PostMapping
    public ResponseEntity<Customer> createCustomer(
            @Validated(Create.class) @RequestBody Customer customer) {
        // 創(chuàng)建客戶邏輯
        return ResponseEntity.ok(customerService.create(customer));
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<Customer> updateCustomer(
            @PathVariable Long id,
            @Validated(Update.class) @RequestBody Customer customer) {
        // 更新客戶邏輯
        return ResponseEntity.ok(customerService.update(id, customer));
    }
}

3.2 分組序列與順序校驗(yàn)

對(duì)于某些復(fù)雜場(chǎng)景,可能需要按特定順序執(zhí)行分組校驗(yàn),確?;掘?yàn)證通過(guò)后才進(jìn)行更復(fù)雜的驗(yàn)證。Spring Validation通過(guò)分組序列(GroupSequence)支持這一需求,開(kāi)發(fā)者可以定義驗(yàn)證組的執(zhí)行順序,一旦某個(gè)組的驗(yàn)證失敗,后續(xù)組的驗(yàn)證將被跳過(guò)。這種機(jī)制有助于提升驗(yàn)證效率,并提供更清晰的錯(cuò)誤反饋。

// 分組序列示例
// 定義基礎(chǔ)分組
public interface BasicCheck {}
public interface AdvancedCheck {}
public interface BusinessCheck {}

// 定義分組序列
@GroupSequence({BasicCheck.class, AdvancedCheck.class, BusinessCheck.class})
public interface OrderedChecks {}

// 使用分組序列
@Entity
public class Order {
    
    @NotNull(groups = BasicCheck.class, message = "訂單號(hào)不能為空")
    private String orderNumber;
    
    @NotEmpty(groups = BasicCheck.class, message = "訂單項(xiàng)不能為空")
    private List<OrderItem> items;
    
    @Valid // 級(jí)聯(lián)驗(yàn)證
    private Customer customer;
    
    @AssertTrue(groups = AdvancedCheck.class, message = "總價(jià)必須匹配訂單項(xiàng)金額")
    public boolean isPriceValid() {
        if (items == null || items.isEmpty()) {
            return true; // 基礎(chǔ)檢查會(huì)捕獲此問(wèn)題
        }
        
        BigDecimal calculatedTotal = items.stream()
            .map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
            
        return totalPrice.compareTo(calculatedTotal) == 0;
    }
    
    @AssertTrue(groups = BusinessCheck.class, message = "庫(kù)存不足")
    public boolean isStockSufficient() {
        // 庫(kù)存檢查邏輯
        return inventoryService.checkStock(this);
    }
    
    // 其他字段和方法
}

// 使用分組序列驗(yàn)證
@Service
public class OrderService {
    
    @Autowired
    private Validator validator;
    
    public ValidationResult validateOrder(Order order) {
        Set<ConstraintViolation<Order>> violations = 
            validator.validate(order, OrderedChecks.class);
            
        // 處理驗(yàn)證結(jié)果
        return processValidationResult(violations);
    }
}

3.3 跨字段校驗(yàn)與類級(jí)約束

有些驗(yàn)證規(guī)則涉及多個(gè)字段的組合邏輯,如密碼與確認(rèn)密碼匹配、起始日期早于結(jié)束日期等。Spring Validation通過(guò)類級(jí)約束解決這一問(wèn)題,允許在類層面定義驗(yàn)證邏輯,處理跨字段規(guī)則。這種方式比單獨(dú)驗(yàn)證各個(gè)字段更加靈活和強(qiáng)大,特別適合復(fù)雜的業(yè)務(wù)規(guī)則。

// 自定義類級(jí)約束注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateRangeValidator.class)
public @interface ValidDateRange {
    
    String message() default "結(jié)束日期必須晚于開(kāi)始日期";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
    
    String startDateField();
    
    String endDateField();
}

// 類級(jí)約束驗(yàn)證器
public class DateRangeValidator implements ConstraintValidator<ValidDateRange, Object> {
    
    private String startDateField;
    private String endDateField;
    
    @Override
    public void initialize(ValidDateRange constraintAnnotation) {
        this.startDateField = constraintAnnotation.startDateField();
        this.endDateField = constraintAnnotation.endDateField();
    }
    
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        try {
            LocalDate startDate = (LocalDate) BeanUtils.getPropertyValue(value, startDateField);
            LocalDate endDate = (LocalDate) BeanUtils.getPropertyValue(value, endDateField);
            
            if (startDate == null || endDate == null) {
                return true; // 空值驗(yàn)證交給@NotNull處理
            }
            
            return !endDate.isBefore(startDate);
        } catch (Exception e) {
            return false;
        }
    }
}

// 應(yīng)用類級(jí)約束
@ValidDateRange(
    startDateField = "startDate",
    endDateField = "endDate",
    groups = BusinessCheck.class
)
public class EventSchedule {
    
    @NotNull(groups = BasicCheck.class)
    private String eventName;
    
    @NotNull(groups = BasicCheck.class)
    private LocalDate startDate;
    
    @NotNull(groups = BasicCheck.class)
    private LocalDate endDate;
    
    // 其他字段和方法
}

四、實(shí)踐應(yīng)用與最佳實(shí)踐

4.1 控制器參數(shù)校驗(yàn)

Spring MVC與Spring Validation的集成提供了便捷的控制器參數(shù)校驗(yàn)。通過(guò)在Controller方法參數(shù)上添加@Valid或@Validated注解,Spring會(huì)自動(dòng)對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行驗(yàn)證。結(jié)合BindingResult參數(shù),可以捕獲校驗(yàn)錯(cuò)誤并進(jìn)行自定義處理。對(duì)于RESTful API,可以使用全局異常處理器統(tǒng)一處理驗(yàn)證異常,返回標(biāo)準(zhǔn)化的錯(cuò)誤響應(yīng)。

// 控制器參數(shù)校驗(yàn)示例
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    // 請(qǐng)求體驗(yàn)證
    @PostMapping
    public ResponseEntity<?> createProduct(
            @Validated(Create.class) @RequestBody Product product,
            BindingResult bindingResult) {
        
        if (bindingResult.hasErrors()) {
            Map<String, String> errors = bindingResult.getFieldErrors().stream()
                .collect(Collectors.toMap(
                    FieldError::getField,
                    FieldError::getDefaultMessage,
                    (msg1, msg2) -> msg1 + "; " + msg2
                ));
                
            return ResponseEntity.badRequest().body(errors);
        }
        
        return ResponseEntity.ok(productService.createProduct(product));
    }
    
    // 路徑變量和請(qǐng)求參數(shù)驗(yàn)證
    @GetMapping("/search")
    public ResponseEntity<?> searchProducts(
            @RequestParam @NotBlank String category,
            @RequestParam @Positive Integer minPrice,
            @RequestParam @Positive Integer maxPrice) {
        
        return ResponseEntity.ok(
            productService.searchProducts(category, minPrice, maxPrice)
        );
    }
}

// 全局異常處理
@RestControllerAdvice
public class ValidationExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        
        Map<String, String> errors = ex.getBindingResult().getFieldErrors().stream()
            .collect(Collectors.toMap(
                FieldError::getField,
                FieldError::getDefaultMessage,
                (msg1, msg2) -> msg1 + "; " + msg2
            ));
            
        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(new ApiError("Validation Failed", errors));
    }
    
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Object> handleConstraintViolation(
            ConstraintViolationException ex) {
        
        Map<String, String> errors = ex.getConstraintViolations().stream()
            .collect(Collectors.toMap(
                violation -> violation.getPropertyPath().toString(),
                ConstraintViolation::getMessage,
                (msg1, msg2) -> msg1 + "; " + msg2
            ));
            
        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(new ApiError("Validation Failed", errors));
    }
}

總結(jié)

Spring Validation通過(guò)標(biāo)準(zhǔn)化的約束注解和靈活的分組校驗(yàn)機(jī)制,為企業(yè)級(jí)應(yīng)用提供了強(qiáng)大的數(shù)據(jù)驗(yàn)證支持。

約束注解的聲明式特性簡(jiǎn)化了驗(yàn)證代碼,而自定義約束功能滿足了各種特定業(yè)務(wù)需求。分組校驗(yàn)和分組序列解決了不同場(chǎng)景下的差異化驗(yàn)證問(wèn)題,類級(jí)約束則實(shí)現(xiàn)了復(fù)雜的跨字段驗(yàn)證邏輯。

在實(shí)際應(yīng)用中,結(jié)合控制器參數(shù)校驗(yàn)和全局異常處理,可以構(gòu)建出既健壯又易用的驗(yàn)證體系。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring詳細(xì)講解循環(huán)依賴是什么

    Spring詳細(xì)講解循環(huán)依賴是什么

    這篇文章主要介紹了Java中的Spring循環(huán)依賴詳情,文章基于Java的相關(guān)資料展開(kāi)詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Java如何獲取當(dāng)前年份、月份和日期字符串

    Java如何獲取當(dāng)前年份、月份和日期字符串

    Java獲取當(dāng)前年份、月份和日期是通過(guò)Calendar類的實(shí)例對(duì)象來(lái)獲取的,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • springcloud微服務(wù)基于redis集群的單點(diǎn)登錄實(shí)現(xiàn)解析

    springcloud微服務(wù)基于redis集群的單點(diǎn)登錄實(shí)現(xiàn)解析

    這篇文章主要介紹了springcloud微服務(wù)基于redis集群的單點(diǎn)登錄實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Apache?Commons?CLI構(gòu)建命令行應(yīng)用利器教程

    Apache?Commons?CLI構(gòu)建命令行應(yīng)用利器教程

    這篇文章主要為大家介紹了構(gòu)建命令行應(yīng)用利器Apache?Commons?CLI的使用教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 14個(gè)編寫(xiě)Spring MVC控制器的實(shí)用小技巧(吐血整理)

    14個(gè)編寫(xiě)Spring MVC控制器的實(shí)用小技巧(吐血整理)

    這篇文章主要介紹了14個(gè)編寫(xiě)Spring MVC控制器的實(shí)用小技巧(吐血整理),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Spring Boot 配置大全(小結(jié))

    Spring Boot 配置大全(小結(jié))

    本篇文章主要介紹了Spring Boot 配置大全(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • Mybatis-Plus3.2.0 MetaObjectHandler 無(wú)法進(jìn)行公共字段全局填充

    Mybatis-Plus3.2.0 MetaObjectHandler 無(wú)法進(jìn)行公共字段全局填充

    這篇文章主要介紹了Mybatis-Plus3.2.0 MetaObjectHandler 無(wú)法進(jìn)行公共字段全局填充,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Java中Stream流中map和forEach的區(qū)別詳解

    Java中Stream流中map和forEach的區(qū)別詳解

    本文主要介紹了Java中Stream流中map和forEach的區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Java 根據(jù)url下載網(wǎng)絡(luò)資源

    Java 根據(jù)url下載網(wǎng)絡(luò)資源

    這篇文章主要介紹了Java 根據(jù)url下載網(wǎng)絡(luò)資源的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11
  • spring boot中的properties參數(shù)配置詳解

    spring boot中的properties參數(shù)配置詳解

    這篇文章主要介紹了spring boot中的properties參數(shù)配置,需要的朋友可以參考下
    2017-09-09

最新評(píng)論