侧边栏壁纸
博主头像
小白博主等级

just do it!

  • 累计撰写 60 篇文章
  • 累计创建 77 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

通用接口校验(自定义注解实现)--持续更新ing

小白
2019-12-01 / 0 评论 / 0 点赞 / 118 阅读 / 1,308 字

介绍

软件开发过程中,对于接口的参数校验时必不可少的,传统的校验都是在代码中使用一连串的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;
}
0

评论区