SpringMVC自定義註解進行引數校驗
在我的另一篇部落格中(SpringMVC),學習瞭如何使用Spring MVC結合Hibernate的校驗框架validation(它和hibernate沒有任何關係)對引數進行校驗。在實際專案中,引數的校驗邏輯可能比較複雜,這時我們可以自定義註解來實現引數校驗,下面是一個簡單的例子。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wuy</groupId> <artifactId>custom-validation</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>custom-validation</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- Web起步依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 測試相關 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- hibernate validator相關依賴 --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.3.2.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <!-- lombok簡化程式碼 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <!-- JSON --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.51</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml
server:
port: 8080
程式啟動類
package com.wuy.customvalidation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class CustomValidationApplication { public static void main(String[] args) { SpringApplication.run(CustomValidationApplication.class, args); } }
User.java
package com.wuy.customvalidation.entity; import com.wuy.customvalidation.validation.Phone; import lombok.Data; import javax.validation.constraints.NotBlank; import java.io.Serializable; @Data public class User implements Serializable { private static final long serialVersionUID = -1L; @NotBlank(message = "使用者名稱不能為空") private String userName; @Phone(message = "手機號不正確") private String phone; }
這個類就是我們要進行驗證的POJO,@Phone是我們自定義的註解,用於對phone這個欄位進行驗證。我們看看@Phone的原始碼。
編寫自定義註解
package com.wuy.customvalidation.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
@Constraint(validatedBy = PhoneConstraintValidator.class)
public @interface Phone {
String message() default "Phone num is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Documented、@Retention、@Target是元註解,這裡不做深入,message方法指定驗證失敗時的錯誤資訊,預設是“Phone num is invalid”。我們主要看看@Constraint註解,它有一個validatedBy屬性,用於指定具體進行驗證的類,PhoneConstraintValidator的原始碼如下:
package com.wuy.customvalidation.validation;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneConstraintValidator implements ConstraintValidator<Phone, String> {
@Override
public void initialize(Phone constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return false;
}
return value.matches("^\\d{11}$");
}
}
PhoneConstraintValidator類實現了ConstraintValidator介面,第一個泛型指定自定義的註解Phone這個類,第二個泛型指定要驗證的引數的型別,這裡是String。initialize方法用於做一些初始化,具體的驗證邏輯寫在isValid方法裡面,這個方法返回false表示驗證失敗,返回true表示驗證成功。這裡的驗證邏輯是,手機號符合11位數字即可。
編寫Controller
程式碼如下:
package com.wuy.customvalidation.controller;
import com.alibaba.fastjson.JSON;
import com.wuy.customvalidation.entity.User;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.stream.Collectors;
@RestController
public class TestController {
@RequestMapping("/save")
@ResponseBody
public String save(@Valid User user, BindingResult errors) {
if (errors.hasErrors()) {
errors.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.forEach(System.out::println);
return JSON.toJSONString(errors.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining("; ")));
}
return JSON.toJSONString(user);
}
}
@Valid註解用於標註需要進行驗證的POJO,BindingResult用於捕獲驗證失敗的資訊。注意:@Valid和BindingResult是配對出現,並且順序是固定的(一前一後)。方法中,首先判斷是否有錯誤資訊,如果有,先是將錯誤資訊全部列印到控制檯,然後將錯誤資訊以“;”分割後返回給呼叫者;如果驗證通過,則直接返回User物件。這裡用到了Java 8的Stream API,關於Java 8的新特性,可以參考我相應的文章。
至此,自定義註解進行引數校驗即完成。