Spring?Validation實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例
一、什么是 Spring Validation
在開發(fā)中,我們經(jīng)常遇到參數(shù)校驗(yàn)的需求,比如用戶注冊(cè)的時(shí)候,要校驗(yàn)用戶名不能為空、用戶名長度不超過20個(gè)字符、手機(jī)號(hào)是合法的手機(jī)號(hào)格式等等。
如果使用普通方式,我們會(huì)把校驗(yàn)的代碼和真正的業(yè)務(wù)處理邏輯耦合在一起,而且如果未來要新增一種校驗(yàn)邏輯也需要在修改多個(gè)地方。
而spring validation允許通過注解的方式來定義對(duì)象校驗(yàn)規(guī)則,把校驗(yàn)和業(yè)務(wù)邏輯分離開,讓代碼編寫更加方便。
Spring Validation其實(shí)就是對(duì)Hibernate Validator進(jìn)一步的封裝,方便在Spring中使用。
Spring提供了數(shù)種數(shù)據(jù)校驗(yàn)的方式:
- 實(shí)現(xiàn)org.springframework.validation.Validator接口,調(diào)用接口實(shí)現(xiàn)類;
- 通過 注解 方式進(jìn)行數(shù)據(jù)校驗(yàn)(按照Bean Validation方式);
- 基于 方法(函數(shù)) 實(shí)現(xiàn)數(shù)據(jù)校驗(yàn);
- 自定義校驗(yàn)
二、實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)
??準(zhǔn)備相關(guān)jar包
引入Maven依賴
:
<dependencies> <!-- validator校驗(yàn)相關(guān)依賴 --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>7.0.5.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>jakarta.el</artifactId> <version>4.0.1</version> </dependency> </dependencies>
注意:上述依賴還不夠,請(qǐng)記得添加spring的基礎(chǔ)依賴和Junit測(cè)試等依賴
?Validator接口方式
這是一個(gè)用于測(cè)試校驗(yàn)的實(shí)體類
:
/** * @author .29. * @create 2023-03-01 10:58 */ //創(chuàng)建實(shí)體類,定義屬性及方法,供校驗(yàn)測(cè)試 public class Person { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
這是一個(gè)實(shí)現(xiàn)了Validator接口的實(shí)體類
:
import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; /** * @author .29. * @create 2023-03-01 11:00 */ //validator接口方式實(shí)現(xiàn) 校驗(yàn) public class PersonValidator implements Validator { @Override public boolean supports(Class<?> aClass) { return Person.class.equals(aClass); //比對(duì)實(shí)體類的類對(duì)象與參數(shù)是否一致 } @Override public void validate(Object o, Errors errors) { //重寫校驗(yàn)方法 //設(shè)置name為空時(shí),報(bào)錯(cuò):name.empty ValidationUtils.rejectIfEmpty(errors,"name","name.empty"); //傳入對(duì)象,強(qiáng)轉(zhuǎn)為實(shí)體類Person對(duì)象 Person p = (Person)o; if(p.getAge() < 0){ //設(shè)置age屬性小于零時(shí)報(bào)錯(cuò) errors.rejectValue("age","error (age < 0)"); }else if(p.getAge() > 110){//設(shè)置age屬性大于110報(bào)錯(cuò) errors.rejectValue("age","error (age > 110) too old !!"); } } }
校驗(yàn)測(cè)試
:
import org.springframework.validation.BindingResult; import org.springframework.validation.DataBinder; /** * @author .29. * @create 2023-03-01 11:21 */ public class testPersonValidator { public static void main(String[] args){ Person person = new Person(); // person.setName("高啟強(qiáng)"); // person.setAge(29); //創(chuàng)建person對(duì)象的DataBinder DataBinder binder = new DataBinder(person); //設(shè)置校驗(yàn) binder.setValidator(new PersonValidator()); //校驗(yàn)(當(dāng)person屬性值為空時(shí),校驗(yàn)不通過) binder.validate(); //輸出校驗(yàn)結(jié)果 //binder.getBindingResult() 獲取校驗(yàn)結(jié)果對(duì)象 //bindingResult.getAllErrors() 獲取所有校驗(yàn)到的錯(cuò)誤 BindingResult bindingResult = binder.getBindingResult(); System.out.println(bindingResult.getAllErrors()); } }
沒有傳入?yún)?shù)時(shí),對(duì)象為空,得到 name.empty 的校驗(yàn)錯(cuò)誤:
[Field error in object ‘target’ on field ‘name’: rejected value [null]; codes [name.empty.target.name,name.empty.name,name.empty.java.lang.String,name.empty]; arguments []; default message [null]]
傳入正確參數(shù)后,沒有校驗(yàn)錯(cuò)誤可輸出:
[]
?基于注解方式(Bean Validation)
使用Bean Validation校驗(yàn)方式,就是如何將Bean Validation需要使用的javax.validation.ValidatorFactory 和javax.validation.Validator注入到容器中。spring默認(rèn)有一個(gè)實(shí)現(xiàn)類LocalValidatorFactoryBean
,它實(shí)現(xiàn)了上面Bean Validation中的接口,并且也實(shí)現(xiàn)了org.springframework.validation.Validator接口。
這是一個(gè)配置了配置LocalValidatorFactoryBean的配置類
:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; /** * @author .29. * @create 2023-03-01 11:34 */ //配置類 @Configuration //聲明為Spring配置類 @ComponentScan("com.haojin.spring.config") //設(shè)置需要掃描的包 public class ValidationConfig { @Bean //返回值放入ioc容器 public LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); } }
這是一個(gè)用于測(cè)試校驗(yàn)的實(shí)體類
:
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; /** * @author .29. * @create 2023-03-01 11:36 */ //實(shí)體類,使用注解定義校驗(yàn)規(guī)則 public class User { @NotNull //不可為空 private String name; @Min(0) //最小值 @Max(110) //最大值 private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
這里總結(jié)一下常見的注解:
- @NotNull 限制必須不為null
- @NotEmpty 只作用于字符串類型,字符串不為空,并且長度不為0
- @NotBlank 只作用于字符串類型,字符串不為空,并且trim()后不為空串
- @DecimalMax(value) 限制必須為一個(gè)不大于指定值的數(shù)字
- @DecimalMin(value) 限制必須為一個(gè)不小于指定值的數(shù)字
- @Max(value) 限制必須為一個(gè)不大于指定值的數(shù)字
- @Min(value) 限制必須為一個(gè)不小于指定值的數(shù)字
- @Pattern(value) 限制必須符合指定的正則表達(dá)式
- @Size(max,min) 限制字符長度必須在min到max之間
- @Email 驗(yàn)證注解的元素值是Email,也可以通過正則表達(dá)式和flag指定自定義的email格式
接下來設(shè)置校驗(yàn)器(兩種方式):
方式一:使用java原生的jakarta.validation.Validator校驗(yàn)
校驗(yàn)器一號(hào)
:
import jakarta.validation.ConstraintViolation; import jakarta.validation.Validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Set; /** * @author .29. * @create 2023-03-01 11:39 */ //使用java原生的jakarta.validation.Validator校驗(yàn) @Service public class MyService1 { @Autowired //自動(dòng)裝配Validator對(duì)象 private Validator validator; //校驗(yàn)方法 public boolean validator(User user){ //校驗(yàn)后的結(jié)果存放進(jìn)Set集合 Set<ConstraintViolation<User>> set = validator.validate(user); //若沒有校驗(yàn)到錯(cuò)誤,集合為空,返回true。 return set.isEmpty(); } }
方式二:用spring提供的validator校驗(yàn)
校驗(yàn)器一號(hào)
:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.validation.BindException; import org.springframework.validation.Validator; /** * @author .29. * @create 2023-03-02 9:24 */ //使用spring提供的validate校驗(yàn)方法 @Service public class MyService2 { @Autowired private Validator validator; public boolean validator2(User user){ BindException bindException = new BindException(user,user.getName()); validator.validate(user,bindException); //調(diào)用校驗(yàn)方法進(jìn)行校驗(yàn) System.out.println(bindException.getAllErrors()); //輸出所有錯(cuò)誤信息 return bindException.hasErrors(); //若沒有異常,返回false } }
創(chuàng)建測(cè)試類,分別測(cè)試兩種校驗(yàn)器
:
import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author .29. * @create 2023-03-02 9:33 */ public class testBeanValidator { @Test public void testValidatorOne(){ //獲取context對(duì)象 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class); //校驗(yàn)器的實(shí)現(xiàn)類對(duì)象 MyService1 myValidatorOne = context.getBean(MyService1.class); User user = new User(); boolean validator = myValidatorOne.validator(user); System.out.println(validator); } @Test public void testValidatorTwo(){ //獲取context對(duì)象 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ValidationConfig.class); //校驗(yàn)器的實(shí)現(xiàn)類對(duì)象 MyService2 myValidatorTwo = annotationConfigApplicationContext.getBean(MyService2.class); User user = new User(); boolean validator = myValidatorTwo.validator2(user); System.out.println(validator); } }
?基于方法的方式
這是一個(gè)配置了MethodValidationPostProcessor的配置類
:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; /** * @author .29. * @create 2023-03-02 18:28 */ //配置了MethodValidationPostProcessor的配置類 @Configuration @ComponentScan("com.haojin.spring.three") public class validationPostProcessor { @Bean public MethodValidationPostProcessor methodValidationPostProcessor(){ return new MethodValidationPostProcessor(); } }
這是一個(gè)測(cè)試校驗(yàn)的實(shí)體類,校驗(yàn)規(guī)則通過注解設(shè)置
import jakarta.validation.constraints.*; /** * @author .29. * @create 2023-03-02 18:30 */ public class User { @NotNull private String name; @Min(0) @Max(129) private int age; //手機(jī)號(hào)格式 1開頭 第二位是(3、4、6、7、9)其一,后面是9位數(shù)字 @Pattern(regexp = "^1(3|4|6|7|9)\\d{9}$",message = "手機(jī)號(hào)碼格式錯(cuò)誤") @NotBlank(message = "手機(jī)號(hào)碼不能為空") private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", phone='" + phone + '\'' + '}'; } }
定義Service類,通過注解操作對(duì)象
import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; /** * @author .29. * @create 2023-03-02 18:35 */ @Service @Validated public class MyService { //定義Service類,通過注解操作對(duì)象 public String testParams(@NotNull @Valid User user){ return user.toString(); } }
測(cè)試
:
import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author .29. * @create 2023-03-02 18:37 */ public class TestMethod { @Test public void testMyService(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(validationPostProcessor.class); MyService bean = context.getBean(MyService.class); User user = new User(); bean.testParams(user); } }
?自定義校驗(yàn)
自定義設(shè)置校驗(yàn)規(guī)則的注解
:
import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {CannotBlankValidator.class}) public @interface CannotBlank { //默認(rèn)錯(cuò)誤消息 String message() default "不能包含空格"; //分組 Class<?>[] groups() default {}; //負(fù)載 Class<? extends Payload>[] payload() default {}; //指定多個(gè)時(shí)使用 @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { CannotBlank[] value(); } }
編寫校驗(yàn)類
:
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> { @Override public void initialize(CannotBlank constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) { //null時(shí)不進(jìn)行校驗(yàn) if (value != null && value.contains(" ")) { //獲取默認(rèn)提示信息 String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate(); System.out.println("default message :" + defaultConstraintMessageTemplate); //禁用默認(rèn)提示信息 context.disableDefaultConstraintViolation(); //設(shè)置提示語 context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation(); return false; } return true; } }
到此這篇關(guān)于 Spring Validation 數(shù)據(jù)校驗(yàn)的文章就介紹到這了,更多相關(guān) Spring Validation 數(shù)據(jù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)字符串四則運(yùn)算公式解析工具類的方法
今天小編就為大家分享一篇java實(shí)現(xiàn)字符串四則運(yùn)算公式解析工具類的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07詳解如何把Java中if-else代碼重構(gòu)成高質(zhì)量代碼
這篇文章主要介紹了詳解如何把Java中if-else代碼重構(gòu)成高質(zhì)量代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11高并發(fā)環(huán)境下安全修改同一行數(shù)據(jù)庫數(shù)據(jù)的策略分享
隨著互聯(lián)網(wǎng)技術(shù)的發(fā)展,越來越多的應(yīng)用需要在高并發(fā)環(huán)境中運(yùn)行,數(shù)據(jù)庫的并發(fā)控制成為了業(yè)務(wù)的關(guān)鍵,本文將介紹如何在高并發(fā)情況下,安全地修改數(shù)據(jù)庫中的同一行數(shù)據(jù),需要的可以參考一下2023-06-06Java對(duì)象傳遞與返回的細(xì)節(jié)問題詳析
我們知道這是一個(gè)核心概念,在Java中總是按值傳遞而不是按引用傳遞,下面這篇文章主要給大家介紹了關(guān)于Java對(duì)象傳遞與返回的細(xì)節(jié)問題的相關(guān)資料,需要的朋友可以參考下2022-11-11