1. 程式人生 > >SpringMVC自定義註解進行引數校驗

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的新特性,可以參考我相應的文章。

至此,自定義註解進行引數校驗即完成。