一、常用注解
常用注解 | 说明 | 判断和示例 |
---|---|---|
@NotNull | 被注解元素不能为null | if(obj != null) |
@NotEmpty | 被注解元素不能为null且长度不能为0 | 字符串:if(s != null && s.length() > 0) 集合:if(obj != null && obj.size() > 0) |
@NotBlank | 只能用于String类型,被注解元素不能为null且必须有非空格外的其它字符 | if(s != null && s.trim().length() > 0) |
@Length(min=low, max=high) | 只能用于String类型,其长度min <= len <= max。按理说都有length了,肯定不能为null,但是测试不行,故必须与@NotNull等非空注解配合使用 | if(s != null && s.length() >= min && s.length <= high) |
@Size(min=low, max=high) | 用于集合类型,其大小min <= size <= max。也必须配合非空注解使用 | if(obj != null && obj.length() >= min && obj.length <= high) |
@Max(max)@Min(min) | 都只能用于Number类型或字符类型。@Max表示被注解元素的值不能超过设置的最大值max;@Min表示被注解的值不能小于min。也必须配合@NotNull注解使用 | 如设置vlanID,最大为4096 |
只用于String类型。需配合非空注解使用 | 默认的格式:a@b | |
@Pattern(regexp=正则表达式) | 用于String类型,regexp设置正则,需配合非空注解使用 | 如校验IP |
二、全局异常
0. 依赖管理
需引入依赖包hibernate-validator
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
1. BindException / MethodArgumentNotValidException
Controller中进行参数校验时抛出该异常。其中,不涉及组操作时,默认抛出BindException异常;涉及组操作时默认抛出MethodArgumentNotValidException异常,差别不大,新版本中MethodArgumentNotValidException继承于BindException,两个异常可以共用一个处理方法
2. ConstraintViolationException
Service中进行参数校验时抛出此异常,通过其getMessage()方法就可获取所有异常message
3. 代码实现
@RestControllerAdvice/@ControllerAdvice:全局异常处理注解,拦截所有异常统一处理。也是 @Component注解,两者的区别同@RestController/@Controller一样。
@ExceptionHandler:拦截指定异常并进行处理,同一个异常处理不能出现两次
@ResponseStatus:设置response的状态码,默认200 OK
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultBack handleConstraintViolationException(ConstraintViolationException ex) {
BasicLogUtil.info("ConstraintViolationException");
return new ResultBack(ResultStatus.FAILED, ex.getMessage());
}
// BindException 和 MethodArgumentNotValidException 共用一个异常处理方法
@ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultBack handleBindException(BindException ex) {
BasicLogUtil.info("BindException");
StringBuilder errorMsg = new StringBuilder();
// 获取所有字段验证出错的信息
List<FieldError> allErrors = ex.getFieldErrors();
allErrors.forEach(fieldError -> {
errorMsg.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append("; ");
});
return new ResultBack(ResultStatus.FAILED, errorMsg.toString());
}
// 同一个异常不能出现在两个@ExceptionHandler注解值中
// @ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultBack handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// 新版本中MethodArgumentNotValidException继承于BindException,而不是直接继承Exceprtion
BasicLogUtil.info("MethodArgumentNotValidException extends BindException");
StringBuilder errorMsg = new StringBuilder();
// 获取所有错误信息(包括字段验证错误信息FieldError)
List<ObjectError> allErrors = ex.getAllErrors();
// 遍历,拼接获取的error的message
allErrors.forEach(error -> {
if(error instanceof FieldError) {
FieldError fieldError = (FieldError) error;
errorMsg.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append("; ");
} else {
errorMsg.append(error.getDefaultMessage()).append("; ");
}
});
return new ResultBack(ResultStatus.FAILED, errorMsg.toString());
}
}
三、示例一(无组操作)
0. 测试Bean类
@Data
public class TestBean {
/**
* 主键
*/
private Integer id;
/**
* 编号
*/
@NotEmpty(message = "编号不能为null")
private String numberIdl;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空")
@Length(min = 3, max = 10)
private String name;
/**
* 修改人
*/
@NotNull(message = "操作人不能为null")
private String modifier;
}
1. controller层验证
直接在参数前加上注解@Valid即可
@RequestMapping("/test")
public Object test(@Valid @RequestBody TestBean testBean) {
System.out.println(testBean);
return testBean;
}
2. service层验证
只需在service接口上添加注解即可,service接口的实现类不用添加,具体添加方式为:
- service接口添加@Validated注解
- 具体方法要校验的参数添加@Valid注解
@Validated
public interface TestService {
Object test(@Valid TestBean testBean);
}
接口的实现类,无需任何注解
@Service
public class TestServiceImpl implements TestService {
@Override
public Object test(TestBean testBean) {
return testBean;
}
}
也可以将这些注解统一放到service的实现类中实现,不过由于override的关系,接口中的test方法参数也必须使用@Valid注解,如下:
public interface TestService {
// 由于继承重写该方法,故此处必须和重写的方法参数保持一致
Object test(@Valid TestBean testBean);
}
@Service
@Validated
public class TestServiceImpl implements TestService {
@Override
public Object test(@Valid TestBean testBean) {
return testBean;
}
}
3. 总结
- Controller里的验证直接添加@Valid注解就行
- Service里的验证说了那么多,其实就是两个层面的注解:
service接口/实现类层面上的@Validated注解;
方法参数层面上校验参数的@Valid注解;
具备这两个层面的注解就行,可以随意组合,如上面所示
四、示例二(组处理)
0. 测试Bean类
@Data
public class TestBeanWithGroup {
/**
* 主键
*/
@NotNull(groups = {GroupUpt.class}, message = "修改时主键不能为null")
private Integer id;
/**
* 编号
*/
@NotNull(groups = {GroupAdd.class}, message = "新增时编号不能为null")
private String numberId;
/**
* 姓名
*/
@NotNull(groups = {GroupAdd.class}, message = "新增时姓名不能为null")
private String name;
/**
* 修改人
*/
@NotNull(groups = {GroupAdd.class, GroupUpt.class}, message = "操作人不能为null")
private String modifier;
@NotNull(message = "测试非组操作属性")
private String noGroup;
public interface GroupAdd extends Default{};
public interface GroupUpt extends Default{};
}
上面最后的noGroup属性未指明任何分组,故是默认的分组Default,而分组GroupAdd和GroupUpt都继承了Default,故当校验这两个分组时也会校验noGroup属性,若只想单纯校验分组的属性,可以取消其继承关系。
1. controller层验证
直接在参数前加上注解@Validated,并指明校验的分组。
@RequestMapping("/testGroup")
public Object testWithGroup(@Validated(TestBeanWithGroup.GroupUpt.class) @RequestBody TestBeanWithGroup testBean) {
System.out.println(testBean);
return testBean;
}
2. service层验证
同上面不适用分组时一样,两个层面,不过由于使用了分组还需多一个层面:
- service接口/实现类层面上的@Validated注解
- 方法参数层面上校验参数的@Valid注解
- 方法层面上的@Validated注解,并指定分组
两个层次(接口和实现类)共同拥有这三个层面的注解即可。
若只有前两个层面的注解,即相当于第三个层面指定的分组为Default分组,故只会验证Default分组(默认分组)的属性。
// 校验全部在接口中实现
@Validated
public interface TestGroupService {
// 指明该校验的分组
@Validated(TestBeanWithGroup.GroupAdd.class)
Object test(@Valid TestBeanWithGroup testBeanWithGroup);
}
// 实现类中指明校验的分组
@Validated
public interface TestGroupService {
Object test(@Valid TestBeanWithGroup testBeanWithGroup);
}
@Service
public class TestGroupServiceImpl implements TestGroupService {
@Override
@Validated(TestBeanWithGroup.GroupAdd.class)
public Object test(TestBeanWithGroup testBeanWithGroup) {
return testBeanWithGroup;
}
}