首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring MVC 整合 Hibernate Validator 實現參數校驗(JSR-303/JSR-380)

     編輯:Java 綜合教學

依賴坐標

<!-- Spring Boot 2.x / Spring 5.x 自動傳遞 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 非 Boot 手動添加 -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.5.Final</version>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<!-- 表達式語言支持(@Pattern 等需要) -->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.1-b11</version>
</dependency>

Spring MVC 配置類啟用校驗

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setProviderClass(HibernateValidator.class);
        
        // 綁定國際化消息源(可選)
        validator.setValidationMessageSource(messageSource());
        return validator;
    }
    
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
        ms.setBasenames("classpath:messages/ValidationMessages"); // 國際化資源文件
        ms.setDefaultEncoding("UTF-8");
        ms.setUseCodeAsDefaultMessage(true);
        return ms;
    }
}

XML 配置方式

<mvc:annotation-driven validator="validator"/>

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list><value>classpath:messages/ValidationMessages</value></list>
    </property>
    <property name="useCodeAsDefaultMessage" value="true"/>
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
    <property name="validationMessageSource" ref="messageSource"/>
</bean>

實體類校驗註解

public class UserDTO {
    private Long id;
    
    @Max(value = 20, message = "{val.age.message}")
    private Integer age;
    
    @NotBlank(message = "{username.not.null}")
    @Length(min = 3, max = 6, message = "{username.length}")
    private String username;
    
    @NotBlank(message = "{pwd.not.null}")
    @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,10}$", message = "密碼必須6-10位字母數字組合")
    private String password;
    
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手機號格式不正確")
    private String phone;
    
    @Email(message = "{email.format.error}")
    private String email;
    
    // getter/setter 省略
}

國際化資源文件

# classpath:messages/ValidationMessages.properties
val.age.message=年齡不能超過20歲
username.not.null=用戶名不能為空
pwd.not.null=密碼不能為空
username.length=用戶名長度必須在{min}到{max}之間
email.format.error=郵箱格式不正確

Controller 使用 @Valid + BindingResult

 
                errors.put(e.getField(), e.getDefaultMessage())
            );
            return Result.error(400, "參數校驗失敗", errors);
        }
        
        // 2. 業務邏輯
        userService.register(dto);
        return Result.success();
    }
    
    // 獲取錯誤詳情工具方法
    private Map<String, String> getErrors(BindingResult result) {
        Map<String, String> map = new HashMap<>();
        result.getFieldErrors().forEach(e -> map.put(e.getField(), e.getDefaultMessage()));
        return map;
    }
}

常用校驗註解速查表

註解包適用類型說明
@NotNulljavax.validation任意值不為 null
@NotBlankorg.hibernate.validatorCharSequence非 null、trim 後長度 > 0 (推薦字符串用此)
@NotEmptyorg.hibernate.validatorCollection/Map/Array/CharSequence非 null、size > 0
@Size(min=, max=)javax.validationCollection/Map/Array/CharSequence大小在範圍內
@Length(min=, max=)org.hibernate.validatorCharSequence字符長度在範圍內
@Min/@Maxjavax.validation數值類型最小/最大值
@DecimalMin/@DecimalMaxjavax.validationBigDecimal/CharSequence數值範圍
@Emailorg.hibernate.validatorCharSequence郵箱格式
@Pattern(regexp=)javax.validationCharSequence正則匹配
@Past/@Futurejavax.validationDate/Calendar/Instant過去/未來日期
@Range(min=, max=)org.hibernate.validator數值類型範圍(含邊界)
@Validjavax.validation對象級聯校驗嵌套對象

分組校驗(場景化校驗)

public interface UpdateGroup {}
public interface CreateGroup {}

public class UserDTO {
    @NotNull(groups = UpdateGroup.class, message = "更新時 ID 必填")
    private Long id;
    
    @NotBlank(groups = CreateGroup.class, message = "創建時用戶名必填")
    private String username;
}

// Controller 指定分組
@PostMapping("/create")
public Result<Void> create(@Validated(CreateGroup.class) @RequestBody UserDTO dto) { ... }

@PutMapping("/update")
public Result<Void> update(@Validated(UpdateGroup.class) @RequestBody UserDTO dto) { ... }

自定義校驗註解

// 1. 定義註解
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
@Documented
public @interface ValidPhone {
    String message() default "手機號格式錯誤";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 2. 實現驗證器
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && PHONE_PATTERN.matcher(value).matches();
    }
}

// 3. 使用
@ValidPhone
private String phone;

全局異常處理(MethodArgumentNotValidException)

 errors.put(fe.getField(), fe.getDefaultMessage())
        );
        return Result.error(400, "參數校驗失敗", errors);
    }
    
    @ExceptionHandler(ConstraintViolationException.class) // @Validated 方法參數校驗異常
    public Result<Map<String, String>> handleConstraintViolation(ConstraintViolationException e) {
        Map<String, String> errors = new HashMap<>();
        e.getConstraintViolations().forEach(
            cv -> errors.put(cv.getPropertyPath().toString(), cv.getMessage())
        );
        return Result.error(400, "參數校驗失敗", errors);
    }
}

最佳實踐:
1. 字符串必用 @NotBlank(@NotNull 不檢查空串,@NotEmpty 不 trim)
2. 國際化鍵值分離:message="{key}" 配合 MessageSource,支持多語言
3. 分組校驗:創建/更新場景不同字段要求用 groups 隔離
4. 級聯校驗:嵌套對象加 @Valid 遞歸驗證
5. 全局異常處理:統一返回結構,前端統一提示
6. 正則預編譯:複雜 @Pattern 考慮自定義註解內部編譯 Pattern

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved