我是南城余!阿里云開發者平臺專家博士證書獲得者!
歡迎關注我的博客!一同成長!
一名從事運維開發的worker,記錄分享學習。
專注于AI,運維開發,windows Linux 系統領域的分享!
本章節對應知識庫
https://www.yuque.com/nanchengcyu/java
本內容來自尚硅谷課程,此處在知識庫做了個人理解
————————————————
10、數據校驗:Validation
10.1、Spring Validation概述
在開發中,我們經常遇到參數校驗的需求,比如用戶注冊的時候,要校驗用戶名不能為空、用戶名長度不超過20個字符、手機號是合法的手機號格式等等。如果使用普通方式,我們會把校驗的代碼和真正的業務處理邏輯耦合在一起,而且如果未來要新增一種校驗邏輯也需要在修改多個地方。而spring validation允許通過注解的方式來定義對象校驗規則,把校驗和業務邏輯分離開,讓代碼編寫更加方便。Spring Validation其實就是對Hibernate Validator進一步的封裝,方便在Spring中使用。
在Spring中有多種校驗的方式
第一種是通過實現org.springframework.validation.Validator接口,然后在代碼中調用這個類
第二種是按照Bean Validation方式來進行校驗,即通過注解的方式。
第三種是基于方法實現校驗
除此之外,還可以實現自定義校驗
10.2、實驗一:通過Validator接口實現
第一步 創建子模塊 spring6-validator
第二步 引入相關依賴
<dependencies><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>
第三步 創建實體類,定義屬性和方法
package com.atguigu.spring6.validation.method1;public class Person {private String name;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;}
}
第四步 創建類實現Validator接口,實現接口方法指定校驗規則
package com.atguigu.spring6.validation.method1;import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;public class PersonValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return Person.class.equals(clazz);}@Overridepublic void validate(Object object, Errors errors) {ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");Person p = (Person) object;if (p.getAge() < 0) {errors.rejectValue("age", "error value < 0");} else if (p.getAge() > 110) {errors.rejectValue("age", "error value too old");}}
}
上面定義的類,其實就是實現接口中對應的方法,
supports方法用來表示此校驗用在哪個類型上,
validate是設置校驗邏輯的地點,其中ValidationUtils,是Spring封裝的校驗工具類,幫助快速實現校驗。
第五步 使用上述Validator進行測試
package com.atguigu.spring6.validation.method1;import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;public class TestMethod1 {public static void main(String[] args) {//創建person對象Person person = new Person();person.setName("lucy");person.setAge(-1);// 創建Person對應的DataBinderDataBinder binder = new DataBinder(person);// 設置校驗binder.setValidator(new PersonValidator());// 由于Person對象中的屬性為空,所以校驗不通過binder.validate();//輸出結果BindingResult results = binder.getBindingResult();System.out.println(results.getAllErrors());}
}
10.3、實驗二:Bean Validation注解實現
使用Bean Validation校驗方式,就是如何將Bean Validation需要使用的javax.validation.ValidatorFactory 和javax.validation.Validator注入到容器中。spring默認有一個實現類LocalValidatorFactoryBean,它實現了上面Bean Validation中的接口,并且也實現了org.springframework.validation.Validator接口。
第一步 創建配置類,配置LocalValidatorFactoryBean
@Configuration
@ComponentScan("com.atguigu.spring6.validation.method2")
public class ValidationConfig {@Beanpublic LocalValidatorFactoryBean validator() {return new LocalValidatorFactoryBean();}
}
第二步 創建實體類,使用注解定義校驗規則
package com.atguigu.spring6.validation.method2;import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;public class User {@NotNullprivate String name;@Min(0)@Max(120)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;}
}
常用注解說明
@NotNull 限制必須不為null
@NotEmpty 只作用于字符串類型,字符串不為空,并且長度不為0
@NotBlank 只作用于字符串類型,字符串不為空,并且trim()后不為空串
@DecimalMax(value) 限制必須為一個不大于指定值的數字
@DecimalMin(value) 限制必須為一個不小于指定值的數字
@Max(value) 限制必須為一個不大于指定值的數字
@Min(value) 限制必須為一個不小于指定值的數字
@Pattern(value) 限制必須符合指定的正則表達式
@Size(max,min) 限制字符長度必須在min到max之間
@Email 驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email格式
第三步 使用兩種不同的校驗器實現
(1)使用jakarta.validation.Validator校驗
package com.atguigu.spring6.validation.method2;import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;@Service
public class MyService1 {@Autowiredprivate Validator validator;public boolean validator(User user){Set<ConstraintViolation<User>> sets = validator.validate(user);return sets.isEmpty();}}
(2)使用org.springframework.validation.Validator校驗
package com.atguigu.spring6.validation.method2;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;@Service
public class MyService2 {@Autowiredprivate Validator validator;public boolean validaPersonByValidator(User user) {BindException bindException = new BindException(user, user.getName());validator.validate(user, bindException);return bindException.hasErrors();}
}
第四步 測試
package com.atguigu.spring6.validation.method2;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestMethod2 {@Testpublic void testMyService1() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService1 myService = context.getBean(MyService1.class);User user = new User();user.setAge(-1);boolean validator = myService.validator(user);System.out.println(validator);}@Testpublic void testMyService2() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService2 myService = context.getBean(MyService2.class);User user = new User();user.setName("lucy");user.setAge(130);user.setAge(-1);boolean validator = myService.validaPersonByValidator(user);System.out.println(validator);}
}
10.4、實驗三:基于方法實現校驗
第一步 創建配置類,配置MethodValidationPostProcessor
package com.atguigu.spring6.validation.method3;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;@Configuration
@ComponentScan("com.atguigu.spring6.validation.method3")
public class ValidationConfig {@Beanpublic MethodValidationPostProcessor validationPostProcessor() {return new MethodValidationPostProcessor();}
}
第二步 創建實體類,使用注解設置校驗規則
package com.atguigu.spring6.validation.method3;import jakarta.validation.constraints.*;public class User {@NotNullprivate String name;@Min(0)@Max(120)private int age;@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手機號碼格式錯誤")@NotBlank(message = "手機號碼不能為空")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;}
}
第三步 定義Service類,通過注解操作對象
package com.atguigu.spring6.validation.method3;import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;@Service
@Validated
public class MyService {public String testParams(@NotNull @Valid User user) {return user.toString();}}
第四步 測試
package com.atguigu.spring6.validation.method3;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestMethod3 {@Testpublic void testMyService1() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService myService = context.getBean(MyService.class);User user = new User();user.setAge(-1);myService.testParams(user);}
}
10.5、實驗四:實現自定義校驗
第一步 自定義校驗注解
package com.atguigu.spring6.validation.method4;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 {//默認錯誤消息String message() default "不能包含空格";//分組Class<?>[] groups() default {};//負載Class<? extends Payload>[] payload() default {};//指定多個時使用@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})@Retention(RetentionPolicy.RUNTIME)@Documented@interface List {CannotBlank[] value();}
}
第二步 編寫真正的校驗類
package com.atguigu.spring6.validation.method4;import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {@Overridepublic void initialize(CannotBlank constraintAnnotation) {}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {//null時不進行校驗if (value != null && value.contains(" ")) {//獲取默認提示信息String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();System.out.println("default message :" + defaultConstraintMessageTemplate);//禁用默認提示信息context.disableDefaultConstraintViolation();//設置提示語context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();return false;}return true;}
}