關注公衆號回覆002,有你想要的一切
驗證在任何時候都非常關鍵。考慮將數據驗證作爲業務邏輯開發有利也有弊,Spring 認爲,驗證不應該只在Web 端進行處理,在服務端也要進行相應的處理,可以防止髒數據存入數據庫中,從而避免爲運維同學和測試同學造成更大的困擾,因爲數據造成的bug會更加難以發現,而且開發人員關注點也不會放在數據本身的問題上,所以做服務端的驗證也是非常有必要的。考慮到上面這些問題,Spring 提供了兩種主要類型的驗證:
一個是實現
Validator
接口來創建自定義驗證器,用於服務端數據校驗。一種是通過Spring 對
Bean Validation
支持實現的。
通過使用 Spring Validator 接口進行驗證
Spring 提供 Validator
接口用於驗證對象。Validator 接口通過使用 Errors
對象來工作,以便在驗證時,驗證器可以向 Errors 對象報告驗證失敗。下面是一個簡單的 對象示例
public class Person {
private String name;
private int age;
// get and set...
}
下面一個例子爲 Person 對象提供了一種驗證方式,通過實現了 org.springframework.validation.Validator
接口 的兩個方法:
supports(Class)
: 表示此 Validator 是否能夠驗證提供的類的實例validate(Object, org.springframework.validation.Errors)
: 驗證給定的對象,如果驗證錯誤,則註冊具有給定 Errors 對象。
實現一個 Validator
非常簡單,而且Spring 也提供了 ValidationUtils
工具類幫助進行驗證。下面是一個驗證 Person 對象的例子:
@Component
public class PersonValidator implements Validator {
// 此 Validator 只驗證 Person 實例
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
}
}
上面代碼示例中的靜態方法 rejectIfEmpty()
方法用於拒絕name屬性,當name 屬性是 null 或者是 空串的時候。查看 ValidationUtils 文檔關於它能夠提供的功能。
然後再來編寫配置類 AppConfig
:
@Configuration
@ComponentScan("com.spring.validation")
public class AppConfig {}
使用 @Configuration 註解聲明此類爲配置類(更多 @Configuration 的用法,請參照 原創 | 我被面試官給虐懵了,竟然是因爲我不懂Spring中的@Configuration )
配置@ComponentScan 註解用於自動裝配,默認是使用 basePackages
掃描指定包,字符串表示。
然後對上面的程序進行驗證
public class SpringValidationApplicationTests {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = new Person();
person.setAge(18);
person.setName(null);
PersonValidator personValidator = applicationContext.getBean("personValidator", PersonValidator.class);
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person,"cxuan");
ValidationUtils.invokeValidator(personValidator,person,result);
List<ObjectError> allErrors = result.getAllErrors();
allErrors.forEach(e-> System.out.println(e.getCode()));
}
}
因爲是基於註解的配置,所以使用 AnnotationConfigApplicationContext
上下文啓動類,把配置類 AppConfig 當作參數,然後構建一個Person 類,爲了測試驗證有效性,把 name 設置爲 null,然後通過上下問的 getBean
方法獲得 personValidator 的實例,通過使用 BeanPropertyBindingResult
把 person 綁定爲 cxuan
的名字,然後使用 ValidationUtils
工具類進行驗證,最後把驗證的結果進行檢查。
上面程序經驗證後的結果如下:
org.springframework.validation.ValidationUtils - Invoking validator [com.spring.validation.PersonValidator@37918c79] DEBUG org.springframework.validation.ValidationUtils - Validator found 1 errors name.empty
使用 Bean Validation 進行驗證
從 Spring4 開始,就已經實現對 JSR-349 Bean Validation 的全面支持。Bean Validation API 在 javax.validation.constraints
包中以 Java 註解(例如 @NonNull) 形式定義了一組可用域對象的約束。
通過使用 Bean Validation API ,可以避免耦合到特定的驗證服務提供程序。Spring 對 Bean Validation API 提供了無縫支持,主要使用一些註解進行驗證,下面一起來看一下
定義對象屬性上的驗證約束
首先,將驗證約束應用於域對象屬性。使用maven 配置需要引入對應的依賴
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
之後定義了一些實體類,使用 javax.validation.constraints
包中的註釋進行標註
public class Singer {
@NotNull
@Size(min = 2,max = 60)
private String firstName;
private String lastName;
@NotNull
private Genre genre;
private Gender gender;
get and set...
}
對於 firstName ,定義了兩個約束,第一個約束由 @NotNull
進行控制,它表示該值不能爲空。此外,@Size
註解控制着 firstName 的長度在 2 - 60 之間。@NotNull 還用於 genre 屬性。下面是Genre
和 Gender
的枚舉類
public enum Genre {
POP("P"),
JAZZ("J"),
BLUES("B"),
COUNTRY("C");
private String code;
private Genre(String code){
this.code = code;
}
public String toString(){
return this.code;
}
}
public enum Gender {
MALE("M"),
FEMALE("F");
private String code;
Gender(String code){
this.code = code;
}
@Override
public String toString() {
return this.code;
}
}
Genre 表示歌手所屬的音樂類型,而 Gender 與音樂事業不相關,所以可以爲空
在 Spring 中配置 Bean Validation 支持
爲了在 Spring 的 ApplicationContext 中配置對 Bean Validation API 的支持,可以在Spring 的配置中定義一個 LocalValidatorFactoryBean
的 bean如下
@Configuration
@ComponentScan("com.spring.validation")
public class ValidationConfig {
@Bean
LocalValidatorFactoryBean validatorFactoryBean(){
return new LocalValidatorFactoryBean();
}
}
聲明一個 LocalValidatorFactoryBean
的 bean 是必須的。默認情況下,Spring 會在類路徑下搜索 Hibernate Validator
庫,驗證它是否存在。
下面我們編寫一個爲 Singer 類提供驗證服務的服務類
@Service
public class SingerValidationService {
@Autowired
private Validator validator;
public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
return validator.validate(singer);
}
}
注入一個 javax.validation.Validator
實例(請注意與 Spring 提供的 Validator 接口不同)。一旦定義了 LocalValidatorFactoryBean ,就可以在應用程序中的任意位置創建 Validator 的句柄。要在 POJO 上進行驗證,需要調用 validator.validate
方法,驗證結果以 ConstraintViolation<T>
接口的集合形式返回。下面是上面例子程序的驗證
public class SpringBeanValidationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ValidationConfig.class);
SingerValidationService singerBean = applicationContext.getBean(SingerValidationService.class);
Singer singer = new Singer();
singer.setFirstName("c");
singer.setLastName("xuan");
singer.setGenre(null);
singer.setGender(null);
validateSinger(singer,singerBean);
applicationContext.close();
}
private static void validateSinger(Singer singer,SingerValidationService singerValidationService){
Set<ConstraintViolation<Singer>> violationSet = singerValidationService.validateSinger(singer);
listViolations(violationSet);
}
private static void listViolations(Set<ConstraintViolation<Singer>> violations){
System.out.println("violations.size() = " + violations.size());
for(ConstraintViolation<Singer> violation : violations){
System.out.println("Validation error for property : " + violation.getPropertyPath());
System.out.println("with value : " + violation.getInvalidValue());
System.out.println("with error message : " + violation.getMessage());
}
}
}
上述代碼構建了一個 Singer 類進行驗證,因爲 firstname 屬性的要求是長度介於 2 - 60 之間並且不能爲null,所以這裏只用了一個字符驗證,genre 屬性不能爲null,最核心的驗證方法就是 singerValidationService.validateSinger(singer).
方法,它會調用
public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
return validator.validate(singer);
}
進行驗證,驗證的結果返回的是 ConstraintViolation<Singler>
類型,然後把對應的錯誤信息輸出,上面的錯誤信息是
violations.size() = 2 Validation error for property : firstName with value : c with error message : 個數必須在2和60之間 Validation error for property : genre with value : null with error message : 不能爲null
可以打印出兩個錯誤,並輸出錯誤的屬性、值以及錯誤信息。