阅读更多
1 Demo工程目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| . ├── pom.xml └── src ├── main │ ├── java │ │ └── org │ │ └── liuyehcf │ │ └── spring │ │ └── validate │ │ ├── Application.java │ │ ├── MainController.java │ │ ├── dto │ │ │ └── UserDTO.java │ │ ├── group │ │ │ ├── Create.java │ │ │ └── Update.java │ │ └── service │ │ ├── UserService.java │ │ └── UserServiceImpl.java │ └── resources └── test └── java └── org └── liuyehcf └── spring └── validate └── TestValidate.java
|
2 pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <?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"> <groupId>org.liuyehcf</groupId> <artifactId>spring-validate</artifactId> <version>1.0-SNAPSHOT</version> <modelVersion>4.0.0</modelVersion>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.9.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.9.RELEASE</version> <configuration> <fork>true</fork> <mainClass>org.liuyehcf.spring.validate.Application</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
|
spring-boot-starter-web
依赖项包含了如下三个与validate相关的依赖
org.hibernate:hibernate-validator
com.fasterxml.jackson.core:jackson-databind
org.springframework:spring-context
:提供@Validated
相关注解
3 group
以下两个接口仅作为validator的分组(例如,作为@NotBlank
的groups
属性值),别无它用
1 2 3 4 5 6 7
| package org.liuyehcf.spring.validate.group;
public interface Create { }
|
1 2 3 4 5 6 7
| package org.liuyehcf.spring.validate.group;
public interface Update { }
|
4 DTO
DTO中用validator相关的注解对字段进行标注,并用groups
属性进行分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package org.liuyehcf.spring.validate.dto;
import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.Range; import org.liuyehcf.spring.validate.group.Create; import org.liuyehcf.spring.validate.group.Update;
public class UserDTO { @NotBlank(groups = {Create.class, Update.class}, message = "name cannot be blank") private String name;
@Range(min = 1, max = 100, groups = Create.class, message = "age must between 1 and 100") private Integer age;
@NotBlank(groups = Update.class, message = "address cannot be blank") private String address;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; } }
|
5 Service
下面我们说明一下,如何让validate在Service Level
生效(如果在Controller Level
使用validator,那么只需要在参数处标记@Validated
注解即可。相比于在Service Level
使用validator要简单许多)
UserService用validator相关注解进行标注,以声明式的方式进行参数校验
- 必须,用
@Validated
标记接口(可以指明校验的组别)
- 非必须,用
@Validated
标记方法(可以指明校验的组别),会覆盖类级别的@Validated
注解
- 必须,用
@Valid
标记参数,声明对参数进行校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package org.liuyehcf.spring.validate.service;
import org.liuyehcf.spring.validate.group.Create; import org.liuyehcf.spring.validate.group.Update; import org.liuyehcf.spring.validate.dto.UserDTO; import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
@Validated public interface UserService { @Validated(Create.class) String createUser(@Valid UserDTO userDTO);
@Validated(Update.class) String updateUser(@Valid UserDTO userDTO); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package org.liuyehcf.spring.validate.service;
import org.liuyehcf.spring.validate.dto.UserDTO; import org.springframework.stereotype.Service;
@Service("userService") public class UserServiceImpl implements UserService {
@Override public String createUser(UserDTO userDTO) { return "createUser success"; }
@Override public String updateUser(UserDTO userDTO) { return "updateUser success"; } }
|
6 Controller
在校验参数时,如果参数非法,那么会抛出ConstraintViolationException
异常,错误信息(@NotBlank
等注解指定的提示信息)藏在ConstraintViolation
中,ConstraintViolationException异常对象持有Set<ConstraintViolation>
。每一个异常校验对应着一个ConstraintViolation对象
我们可以先捕获ConstraintViolationException异常,然后取出所有异常信息,拼接成提示信息然后返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package org.liuyehcf.spring.validate;
import org.liuyehcf.spring.validate.dto.UserDTO; import org.liuyehcf.spring.validate.service.UserService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException;
@Controller @RequestMapping("/") public class MainController {
@Resource private UserService userService;
@RequestMapping("/create") @ResponseBody public String createUser(@RequestBody UserDTO userDTO) { try { return userService.createUser(userDTO); } catch (ConstraintViolationException e) { StringBuilder sb = new StringBuilder(); for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) { sb.append(constraintViolation.getMessage()) .append(";"); } if (sb.length() != 0) { sb.setLength(sb.length() - 1); } return sb.toString(); } }
@RequestMapping("/update") @ResponseBody public String updateUser(@RequestBody UserDTO userDTO) { try {
return userService.updateUser(userDTO); } catch (ConstraintViolationException e) { StringBuilder sb = new StringBuilder(); for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) { sb.append(constraintViolation.getMessage()) .append(";"); } if (sb.length() != 0) { sb.setLength(sb.length() - 1); } return sb.toString(); } } }
|
7 Application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package org.liuyehcf.spring.validate;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration @ComponentScan("org.liuyehcf.spring.validate") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
|
8 Validate With Junit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package org.liuyehcf.spring.validate;
import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.liuyehcf.spring.validate.dto.UserDTO; import org.liuyehcf.spring.validate.group.Create;
import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import java.util.Set;
import static org.junit.Assert.assertEquals;
public class TestValidate { private static ValidatorFactory validatorFactory; private static Validator validator;
@BeforeClass public static void createValidator() { validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); }
@AfterClass public static void close() { validatorFactory.close(); }
@Test public void testGroupCreate() { UserDTO userDTO = new UserDTO(); userDTO.setName("小明"); userDTO.setAge(0);
Set<ConstraintViolation<UserDTO>> validates = validator.validate(userDTO, Create.class);
assertEquals(validates.size(), 1); ConstraintViolation<UserDTO> violation = validates.iterator().next();
assertEquals(violation.getPropertyPath().toString(), "age"); } }
|
9 总结
在实际项目当中,我们会把接口以及DTO单独作为一个模块进行维护,此时我们可以用validate注解来告诉接口调用方,哪些参数是必须的,哪些不是必须的,同时也省下了重复的参数校验工作。我们可以在接口模块中引入如下依赖(scope
为provided
,避免造成依赖污染,尽量保持接口模块依赖关系的简洁性)
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <scope>provided</scope> </dependency>
|
10 参考