校验注解的作用
零碎执行业务逻辑之前,会对输出数据进行校验,检测数据是否无效非法的。所以咱们可能会写大量的 if else
等判断逻辑,特地是在不同办法呈现雷同的数据时,校验的逻辑代码会重复呈现,导致代码冗余,浏览性和可维护性极差。
自定义校验注解
引入依赖
Hibernate 框架中有一个组件 hibernate-validator
专门用于数据校验,在平时的 Spring 我的项目中尽管数据层不应用 Hibernate 做 ORM
框架,然而 hibernate-validator
也常常被集成来做数据校验。
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.7.Final</version>
</dependency>
上面咱们写一个用于 URL
校验的注解,实现一个简略的网站信息管理的 URL
校验, 做校验的形式咱们也应用现成的 apache 工具包中提供的校验工具。
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.7</version>
</dependency>
实现注解
校验注解
/**
* 会将注解信息蕴含在 javadoc 中
*/
@Documented
/**
* 1、RetentionPolicy.SOURCE:注解只保留在源文件,当 Java 文件编译成 class 文件的时候,注解被遗弃;* 2、RetentionPolicy.CLASS:注解被保留到 class 文件,但 jvm 加载 class 文件时候被遗弃,* 这是默认的生命周期;* 3、RetentionPolicy.RUNTIME:注解不仅被保留到 class 文件中,jvm 加载 class 文件之后,依然存在;*
* 个别如果须要在运行时去动静获取注解信息,那只能用 RUNTIME 注解,比方 @Deprecated 应用 RUNTIME 注解
* 如果要在编译时进行一些预处理操作,比方生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;* 如果只是做一些查看性的操作,比方 @Override 和 @Deprecated,应用 SOURCE 注解。*/
@Retention(RetentionPolicy.RUNTIME)
/**
* 作用在字段上
* TYPE - 作用在类下面
* FILED - 作用在属性下面
* METHOD - 作用在办法上
* CONSTRUCTION - 作用在结构器上
* PARAM - 作用在办法参数上
* 容许多种的状况是 @Target({ElementType.FIELD,ElementType.METHOD})
*/
@Target(ElementType.FIELD)
/**
* 对应校验类
*/
@Constraint(validatedBy = IsUrlValidator.class)
public @interface IsUrl {
/**
* 是否 强校验
* @return
*/
boolean required() default true;
/**
* 校验不通过返回信息
* @return
*/
String message() default "请输出正确的 url";
/**
* 所属分组, 即在有分组的状况下,只校验所在分组参数
* @return
*/
Class<?>[] groups() default {};
/**
* 次要是针对 bean,很少应用
*
* @return 负载
*/
Class<? extends Payload>[] payload() default {};}
校验类
校验类须要实现 ConstraintValidator<T,E>
接口,第一个泛型为注解,第二个为校验的数据类型。
实现这个接口必须要重写 isValid()
办法,在其中实现次要的校验逻辑。
public class IsUrlValidator implements ConstraintValidator<IsUrl,String> {
private boolean isRequired;
/**
* 初始化,获取是否强校验
* @param constraintAnnotation
*/
@Override
public void initialize(IsUrl constraintAnnotation) {isRequired = constraintAnnotation.required();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext context) {if (!isRequired){return true;}else {UrlValidator validator = UrlValidator.getInstance();
return validator.isValid(s);
}
}
}
应用自定义注解
创立 Insert
、Update
分组别用于辨别和开启校验
用于分组的类须要继承 javax.validation.groups.Default
接口
public interface Update extends Default {}
public interface Insert extends Default {}
创立一个 WebSite
类, 对其中 url
、alternateUrl
进行校验,这个字段别离属于 Insert
分组、Update
分组的时候进行字段校验。
public class WebSite {
/**
* id
*/
private Integer id;
/**
* 网站名称
*/
private String name;
/**
* 网址
*/
@IsUrl(groups = Insert.class)
private String url;
/**
* 备用网址
*/
@IsUrl(groups = Update.class)
private String alternateUrl;
}
具体校验形式如下,在 insert 接口对 Insert
分组进行校验,也就是校验 url
属性, 在 updateAlternate 接口对 Update
分组进行校验,也就是对 alternateUrl
字段进行校验。
@RestController
@RequestMapping("/website")
public class WebSiteController {@RequestMapping("/insert")
public void insert(@RequestBody @Validated(Insert.class) WebSite site){System.out.println(site);
}
@RequestMapping("/updateAlternate")
public void updateAlternateUrl(@RequestBody @Validated(Update.class) WebSite site){System.out.println(site);
}
}
若校验不通过,代码会抛出 MethodArgumentNotValidException
异样,咱们实现一个对立异样解决类来解决这个异样报错,并返回校验提示信息。
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 解决接口参数数据格式谬误异样
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public Object errorHandler(HttpServletRequest request, MethodArgumentNotValidException e) {List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
log.error("{}申请,产生参数校验异样:{}",request.getServletPath(),message);
return message;
}
}
应用 http 工具调用接口,返回相干信息
首先应用一个谬误的 url
参数调用 insert
接口,校验不通过,然而调用 updateAlternate
接口能够通过。
POST http://localhost:8080/website/insert
Content-Type: application/json
{
"id": 1,
"name": "百度",
"url":"htps://www.baidu.com/",
"alternateUrl":"https://www.baidu.com/"
}
###
POST http://localhost:8080/website/updateAlternate
Content-Type: application/json
{
"id": 1,
"name": "百度",
"url":"htps://www.baidu.com/",
"alternateUrl":"https://www.baidu.com/"
}
调用 insert
接口的返回及日志打印如下
HTTP/1.1 200
Content-Type: text/plain;
charset=UTF-8
Content-Length: 21
Date: Wed, 02 Mar 2022 15:30:23
GMTKeep-Alive: timeout=60
Connection: keep-alive
请输出正确的 url
--------------------------------------
xxx.GlobalExceptionHandler : /website/insert 申请,产生参数校验异样:请输出正确的 url