介绍
软件开发过程中,对于接口的参数校验时必不可少的,传统的校验都是在代码中使用一连串的if…else…语句 ;Spring有着一些校验的注解可供开发者使用,在参数前田间这些注解可以达到参数校验功能,然而,通过自定义注解进行切面处理也可以实现类似Spring参数校验注解的功能;本文从Spring参数校验注解以及自己通过SOP实现的自定义参数校验两个方面介绍注解校验参数。
Spring中的参数校验注解
AOP实现通用接口校验(自定义注解)
新建自定义注解@Check
package com.ant.sso.Common.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Check {
String[] value();
}
定义一个注解@Check,被使用类的在方法级别,在运行时有效,注解有一个String类型的属性,用来保存方法需要被验证参数的信息。
新建一个注解枚举类Operator
package com.ant.sso.Common;
import com.ant.sso.Utils.StringUtils;
import lombok.Getter;
import java.util.function.BiFunction;
@Getter
public enum Operator {
GRATER_THAN(" > ",null,"请求参数异常!"),//大于
GRATER_OR_EQUAL_TO(" [ ",null,"请求参数异常!"),//大于等于
LESS_THAN(" < ",null,"请求参数异常!"),//小于
LESS_OR_EQUAL_TO(" ] ",null,"请求参数异常!"),//小于等于
NOT_NULL(" not null ",Operator::notNull,"请求参数异常:请求参数不为空!"),//不为空
NOT_EQUAL(" != ",null,"请求参数异常!"),//不等于
IN(" in ",null,"请求参数异常!"),//参数在几个数之间如 age in 1,2,3 等价于 age==1||age==2||age==3;
NOT_IN(" not in ",null,"请求参数异常!"),//参数不在几个数之间
BETWEEN_INCLUDE(" [] ",null,"请求参数异常!"),//在两个数范围之间(闭区间)
BETWEEN_UN_INCLUDE(" () ",null,"请求参数异常!"),//在两个数范围之间(开区间)
OUT_INCLUDE(" ][ ",null,"请求参数异常!"),//在闭区间之外
OUT_UN_INCLUDE(" )( ",null,"请求参数异常!"),//在开区间之外
;
private String value;
private BiFunction<Object,String,Boolean> fun;
private String errMsg;
Operator(String value,BiFunction<Object,String,Boolean>fun,String msg){
this.value=value;
this.errMsg=msg;
this.fun=fun;
}
private static Boolean notNull(Object value,String operatorNum){
return value != null && ((!(value instanceof String)) || !StringUtils.isEmpty((String) value));
}
}
新建一个参数注解切面类CheckAspect
package com.ant.sso.Config.aspect;
import com.ant.sso.Common.AntException;
import com.ant.sso.Common.AntResponse;
import com.ant.sso.Common.AntResponseCode;
import com.ant.sso.Common.Operator;
import com.ant.sso.Common.annotation.Check;
import lombok.extern.log4j.Log4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Log4j
@Component
@Aspect
public class CheckAspect {
@Pointcut("execution(public * com.ant.sso.Controller..*.*(..))&&@annotation(com.ant.sso.Common.annotation.Check)")//Controller包下及其子包的任意方法,不限参数类型
public void check(){
log.info("进入切入点 check ...");
}
@Around("check()")
public Object checkAround(ProceedingJoinPoint point){
Object obj=null;
try{
System.out.println("around check...");
String errMsg=doCheck(point);
if(errMsg!=null){
AntResponse antResponse=new AntResponse();
antResponse.setError(AntResponseCode.ILLEGAL_PARAMETER,errMsg);
return antResponse;
}else{
obj=point.proceed();
}
}catch (Exception e){
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return obj;
}
/**
* 执行校验
*/
private String doCheck(ProceedingJoinPoint point) throws NoSuchMethodException {
Class<?> targetClass=point.getTarget().getClass();//获取方法所在Controller类的字节码对象
MethodSignature ms= (MethodSignature) point.getSignature();//获取方法签名
Method method=targetClass.getDeclaredMethod(ms.getName(),ms.getParameterTypes());//反射通过方法所在类的字节码对象获取方法对象
Object[] params=point.getArgs();//获取请求参数
Check check=method.getAnnotation(Check.class);//获取方法上的注解
String[] values=check.value();//获取注解的值
String[] paramterNames=ms.getParameterNames();
Map<String,Object> paramters=new HashMap<>();
for(int i=0;i<params.length;i++){
paramters.put(paramterNames[i],params[i]);
}
String errMsg=null;
for(String val:values){
errMsg=resove(val,paramters);
if(errMsg!=null) break;
}
return errMsg;
}
/**
* 操作符
* in :参数在几个数之间如 age in 1,2,3 等价于 age==1||age==2||age==3;
* not in : 参数不在几个数之间
* not null:参数不为空;
* != : 参数不为;
* > :参数大于;
* < :参数小于;
* [ : 参数大于等于
* ] :参数小于等于
* [] : 参数在两个数范围之间(闭区间) 如 age [] 19,25 等价于 age>=19&&age<=25
* () : 参数在两个数范围之间(开区间) 如 age () 19,25 等价于 age>19&&age<25
* ][ : 参数在闭区间之外
* )( : 参数在开区间之外
*/
private String resove(String val,Map<String,Object> paramMap){
Boolean isVailded=true;
Operator operator=getOperator(val);
if(operator==null) throw new AntException(AntResponseCode.CHECK_RULES_EXCEPTION);
String[] vals=val.split(":");
String msg=vals.length>1?vals[1]:operator.getErrMsg();
String[] checkParams=vals[0].split(operator.getValue());
String filedName=checkParams[0];
String checkValue=checkParams.length>1?checkParams[1]:null;
if(filedName.contains(".")){
return null;
}else{
return !paramMap.containsKey(filedName)?msg:operator.getFun().apply(paramMap.get(filedName),checkValue)?null:msg;
}
}
/**
* 获取校验规则操作符枚举类对象
*/
private Operator getOperator(String checkStr){
Operator[] operators=Operator.values();
for(Operator operator:operators){
if(checkStr.contains(operator.getValue()))
return operator;
}
return null;
}
}
使用
@RequestMapping(value = "/test")
@Check(value = {"param1 not null :param1不可以为空","tel not null :请填写手机号!"})
public AntResponse test(String param1,String tel){
AntResponse antResponse=new AntResponse();
antResponse.setSuccess("hello world!");
return antResponse;
}
评论区