環(huán)境:Springboot2.6.12
1. 簡(jiǎn)介
Spring Validation是一種輕量級(jí)的數(shù)據(jù)驗(yàn)證框架,主要用于對(duì)JAVA對(duì)象進(jìn)行校驗(yàn)。它為數(shù)據(jù)驗(yàn)證提供了統(tǒng)一的接口和基本的校驗(yàn)功能,解決了數(shù)據(jù)校驗(yàn)這一常見(jiàn)問(wèn)題,讓開(kāi)發(fā)人員能夠方便地對(duì)數(shù)據(jù)進(jìn)行驗(yàn)證,從而保證數(shù)據(jù)的有效性和安全性。
Spring Validation提供了一套注解,用于對(duì)Java對(duì)象進(jìn)行校驗(yàn),支持嵌套校驗(yàn)和分組校驗(yàn),支持國(guó)際化和自定義注解和校驗(yàn)器,可以滿足各種復(fù)雜的校驗(yàn)需求。它的主要特點(diǎn)包括:
- 輕量級(jí):Spring Validation只做驗(yàn)證相關(guān)的事情,不包含復(fù)雜的業(yè)務(wù)邏輯。
- 簡(jiǎn)單易用:基于注解,簡(jiǎn)潔明了,易于維護(hù)。
- 校驗(yàn)規(guī)則靈活:支持自定義校驗(yàn)規(guī)則,可擴(kuò)展性強(qiáng)。
- 支持國(guó)際化:根據(jù)不同的語(yǔ)言環(huán)境,使用不同的校驗(yàn)提示消息。
- 集成Hibernate Validator:Spring Validation默認(rèn)使用Hibernate Validator作為其具體的實(shí)現(xiàn),可以輕松地與其他數(shù)據(jù)驗(yàn)證框架一起工作。
Bean Validation 為Java應(yīng)用程序提供了一種通過(guò)約束聲明和元數(shù)據(jù)進(jìn)行驗(yàn)證的通用方法。要使用它,只需要對(duì)POJO屬性進(jìn)行注釋?zhuān)缓笥蛇\(yùn)行時(shí)強(qiáng)制執(zhí)行這些約束。有內(nèi)置的約束,你也可以定義自己的自定義約束。如下所示:
public class Person {
@NotNull
@Size(max=64)
private String name;
@Min(0)
private int age;
}
Bean驗(yàn)證驗(yàn)證器然后根據(jù)聲明的約束驗(yàn)證此類(lèi)的實(shí)例。有關(guān)API的一般信息,請(qǐng)參見(jiàn)Bean驗(yàn)證。有關(guān)特定約束,請(qǐng)參閱Hibernate驗(yàn)證程序文檔。
配置Bean驗(yàn)證提供程序
Spring提供了對(duì)Bean驗(yàn)證API的全面支持,包括將Bean驗(yàn)證提供者作為Spring Bean。這使你可以在應(yīng)用程序中需要驗(yàn)證的任何位置注入javax.validation.ValidatorFactory或javax.validation.Validator。
你可以使用LocalValidatorFactoryBean將默認(rèn)驗(yàn)證器配置為Spring Bean,如下例所示:
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Configuration
public class AppConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
上面的示例中的基本配置使用默認(rèn)引導(dǎo)機(jī)制觸發(fā)bean驗(yàn)證進(jìn)行初始化。Bean驗(yàn)證提供程序(如Hibernate驗(yàn)證程序)應(yīng)該出現(xiàn)在類(lèi)路徑中,并被自動(dòng)檢測(cè)到。
2. 注入 Validator
@Service
public class PersonService {
// inject javaee validator object
@Resource
private Validator validator ;
// inject spring validator object
@Resource
private org.springframework.validation.Validator valid ;
}
簡(jiǎn)單實(shí)例
接著上面的配置,我們只需要做驗(yàn)證動(dòng)作即可。
@Service
public class PersonService {
@Resource
private Validator validator ;
@Resource
private org.springframework.validation.Validator valid ;
public void validator(Person person) {
Set<ConstrAIntViolation<Person>> res = validator.validate(person) ;
res.forEach(cv -> {
System.out.println(cv.getMessage()) ;
});
System.out.println("----------------------") ;
BindingResult errors = new MapBindingResult(new HashMap<String, Object>(), "person") ;
valid.validate(person, errors) ;
if (errors.hasErrors()) {
errors.getAllErrors().forEach(oe -> {
System.out.println(oe.getDefaultMessage()) ;
});
}
}
}
測(cè)試
@SpringBootTest
class SpringBootValidationApplicationTests {
@Resource
private PersonService ps ;
@Test
public void testValidator() {
Person person = new Person() ;
person.setAge(-1);
ps.validator(person) ;
}
}
最小不能小于0
不能為null
----------------------
最小不能小于0
不能為null
3. 自定義注解驗(yàn)證
每個(gè)Bean驗(yàn)證約束由兩部分組成:
- 聲明約束及其可配置屬性的@Constraint注釋。
- 實(shí)現(xiàn)約束行為的javax.validation.ConstraintValidator接口的實(shí)現(xiàn)。
要將聲明與實(shí)現(xiàn)關(guān)聯(lián),每個(gè)@Constraint注釋都會(huì)引用相應(yīng)的ConstraintValidator實(shí)現(xiàn)類(lèi)。在運(yùn)行時(shí),當(dāng)域模型中遇到約束注釋時(shí),ConstraintValidatorFactory將實(shí)例化引用的實(shí)現(xiàn)。下面的示例實(shí)現(xiàn)一個(gè)前綴匹配的驗(yàn)證邏輯:
自定義注解
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PrefixConstraintValidator.class)
public @interface PrefixConstraint {
String value() default "" ;
// 這里的{validator.prefix.error}就是資源文件中定義的錯(cuò)誤信息
String message() default "{validator.prefix.error}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
以上的注解屬性都是必須的。
注意message屬性是我們將發(fā)生錯(cuò)誤后錯(cuò)誤信息定義在配置文件中,而該文件的basename必須是ValidationMessages,如果你需要國(guó)際化支持,那么就這樣命名:ValidationMessages_zh_CN.properties。
自定義驗(yàn)證器
public class PrefixConstraintValidator implements ConstraintValidator<PrefixConstraint, CharSequence> {
@Resource
private DataService ds ;
private String prefix ;
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
ds.ak() ;
if ( value == null ) {
return false ;
}
return ((String) value).startsWith(prefix) ;
}
@Override
public void initialize(PrefixConstraint pc) {
prefix = pc.value() ;
}
}
驗(yàn)證器必須實(shí)現(xiàn)ConstraintValidator接口,該接口是泛型接口,第一個(gè)參數(shù)是該驗(yàn)證器要用于在那個(gè)注解,第二個(gè)參數(shù)是該注解應(yīng)用在什么數(shù)據(jù)類(lèi)型上。注意:在自定義驗(yàn)證器中我們是可以隨意地注入其它Bean對(duì)象,是不是很強(qiáng)大?
基于方法級(jí)的驗(yàn)證
你可以通過(guò)MethodValidationPostProcessor Bean定義將Bean validation 1.1(以及Hibernate Validator 4.3的自定義擴(kuò)展)支持的方法驗(yàn)證功能集成到Spring上下文中:
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
@Service
@Validated
public class PersonService {
@NotNull(message = "返回值不能為空")
public Person findPerson(@NotEmpty(message = "ID 不能為空") String id) {
return null ;
}
}
注意:類(lèi)上必須有@Validated注解;因?yàn)樯厦娴腂eanPostProcessor中定義的Advisor(DefaultPointcutAdvisor)使用的切入的Pointcut在類(lèi)級(jí)別上過(guò)濾條件是必須有@Validated注解,而方法則是攔截所有的方法。
測(cè)試
這里是拋出的異常javax.validation.ConstraintViolationException,所有我們需要一個(gè)全局的異常攔截器來(lái)對(duì)異常做處理。
其他配置選項(xiàng)
默認(rèn)的LocalValidatoryFactoryBean配置對(duì)于大多數(shù)情況都足夠了。對(duì)于各種Bean驗(yàn)證構(gòu)造,有許多配置選項(xiàng),從消息插值到遍歷解析。有關(guān)這些選項(xiàng)的更多信息,請(qǐng)參閱LocalValidatorFactoryBean Javadoc。
https://docs.spring.io/spring-framework/docs/5.3.11/javadoc-api/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.html