zoukankan      html  css  js  c++  java
  • 使用AOP和Validator技术对项目接口中的参数进行非空等校验

    javax.validation.Validator基础知识补充:
    validator用来校验注解的生效,如:
       @NotBlank(message = "地址名不能为空")
        private String addressName;
    当addressName这个值为null时,会报message = "地址名不能为空"的值:
    简单demo:
    //对Location对象进行校验
            ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
            Validator validator = validatorFactory.getValidator();
            //因为:Location可能有很多字段都需要校验,所以校验后返回的信息是一个集合
            Set<ConstraintViolation<Location>> sets = validator.validate(location);
            //校验不通过的信息都存到了set集合中,从中可以拿到校验不通过的信息
            if(null!=sets && sets.size()>1){
                for (ConstraintViolation<Location> set : sets) {
                    String message = set.getMessage();//注解的message信息
                    System.out.println(message);
                }
            }
    //所有的校验注解在javax.validation.constraints这个包下
    
    如包下的NotNull注解,源码如下:
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(NotNull.List.class)
    @Documented
    @Constraint(
        validatedBy = {}
    )
    public @interface NotNull {
        String message() default "{javax.validation.constraints.NotNull.message}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public @interface List {
            NotNull[] value();
        }
    }
    //所以,我们也可以自定义注解
    
    //例如:我们的接口只对VIP为白银的用户可用,自定义注解如下
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(
            validatedBy = {CheckVIPValidator.class} //注解要生效,就要有一个类的方法让它生效
    )
    public @interface ViP {
    
    
            String message() default "只有白银用户才能使用";
    
            Class<?>[] groups() default {};
    
            Class<? extends Payload>[] payload() default {};
    
            String value();
    
    
    }
    //使注解生效的类:
    public class CheckVIPValidator implements ConstraintValidator<ViP, String> {
        private  String vip=null;
        @Override
        public void initialize(ViP constraintAnnotation) {
            vip=constraintAnnotation.value();
        }
    
        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            if(s.equals(vip)){
                return true;
            }
            return false;
        }
    }
    
    //下面是APO结合validator技术,本项目是基于springboot的一个简单项目,spring项目也可以:
    
    先看项目结构:

    各对象的数据:
    
    自定义注解和其实现类:
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(
            validatedBy = {CheckVIPValidator.class} //注解要生效,就要有一个类的方法让它生效
    )
    public @interface ViP {
    
    
            String message() default "只有白银用户才能使用";
    
            Class<?>[] groups() default {};
    
            Class<? extends Payload>[] payload() default {};
    
            String value();
    
    
    }
    public class CheckVIPValidator implements ConstraintValidator<ViP, String> {
        private  String vip=null;
        @Override
        public void initialize(ViP constraintAnnotation) {
            vip=constraintAnnotation.value();
        }
    
        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            if(s.equals(vip)){
                return true;
            }
            return false;
        }
    }
    //接口
    
    @Controller
    public class ValidatorController {
    
        @RequestMapping(value = "check/validator",method = RequestMethod.POST)
        @ResponseBody
        public CommonResponseDTO checkValidator(@RequestBody User user) throws Exception {
            CommonResponseDTO beeboxCommonResponseDTO = new CommonResponseDTO();
            return beeboxCommonResponseDTO;
        }
    }
    
    //请求对象:
    public class User {
        @NotNull(message = "名字不能为空")
        private String name;
        @ViP(value ="白银",message ="只有白银用户才能访问")
        private String vip;
        @Min(value = 18,message = "年龄需要大于18岁")
      @NotNull(message="年龄不能为空")
    private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVip() { return vip; } public void setVip(String vip) { this.vip = vip; } } //返回对象: public class CommonResponseDTO { private String code; private String desc; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } //最重要的对象: 切面类: package com.example.demo.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Set; /** * 场景:使用AOP技术对接口(controller下的方法参数进行校验,对贴有校验注解如@NotNull @Min等做校验,不通过的话,就返回对应错误码和描述) * 按照项目规范:请求参数只能是一个值,返回参数必须含有一个错误码字段和一个错误码描述字段 * * * * */ @Component @Aspect
    @Order(1) //当有多个切面时,加上这个顺序,就可以控制前后顺序了
    public class AOPAspect { /** * //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点 * com.sample.service.impl..*.*(..) * 整个表达式可以分为五个部分: * 1、execution(): 表达式主体。 * 2、第一个*号:表示返回类型,*号表示所有的类型。 * 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。 * 4、第二个*号:表示类名,*号表示所有的类。 * 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。 */ @Pointcut("execution(* com.example.demo.controller.*.*(..))") public void declareJoinPointerExpression() {} /** * ProceedingJoinPoint 是 JoinPoint的子接口,有2个方法 * Object proceed() throws Throwable //执行目标方法 Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法 * * JoinPoint的几个参数: * Signature getSignature(); //封装了方法相关的信息 * signature.getDeclaringType();//方法所在的字节码对象 * signature.getDeclaringTypeName(); 方法所在的类名(包.类这种形式) * signature.getModifiers();//获取方法的修饰符的数字表示,如public修饰符返回1,private... * signature.getName();//获取方法的名称 * //Signature的实现类是MethodSignature 可以获得方法的相关数据,如参数,返回值类型,方法等 * MethodSignature signature = (MethodSignature)joinPoint.getSignature(); Method method = signature.getMethod();//获取方法 Class returnType = signature.getReturnType();//获取返回值的类型 *joinPoint.getArgs();//获取方法的参数 * * joinPoint.getKind();//返回的是JoinPoint对象里面的常量,如method-execution(方法的执行) * joinPoint.getStaticPart();//获取的是JoinPoint里面的一个内部接口 * joinPoint.getTarget(); //获取被代理对象 * joinPoint.getThis(); //获取的是代理对象 * * @param joinPoint */ @Before("declareJoinPointerExpression()") public void beforeMethod(JoinPoint joinPoint){ System.out.println("增强前"); joinPoint.getThis(); } @After("declareJoinPointerExpression()") public void AferMethod(JoinPoint joinPoint){ System.out.println("增强后..."); } //本次校验的场景是contorller的方法只有一个参数,并且放回值都有状态和描述2个字段 @Around("declareJoinPointerExpression()") public Object aroundMethod(JoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature)joinPoint.getSignature(); //通过该对象可以获取方法返回值 Class returnType = signature.getReturnType();//方法返回类型 Object[] args = joinPoint.getArgs(); //本次实验只能有一个请求参数 if(null==args || args.length!=1){ return getReturnObject(returnType, "400", "参数数目不对"); } Object arg = args[0];//获取请求参数 ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); Validator validator = validatorFactory.getValidator(); Set<ConstraintViolation> violations = validate(validator, arg);//校验参数,异常会封装到set集合 StringBuilder sb = new StringBuilder();//拼接异常数据 if(null!=violations || violations.size()>1){ sb.append("基础数据校验不通过:"); for (ConstraintViolation set : violations) { String message = set.getMessage(); //异常数据信息 sb.append(message).append("</br>"); } return getReturnObject(returnType,"400",sb.toString()); }else { ProceedingJoinPoint pjp= (ProceedingJoinPoint) joinPoint; //如果没有异常数据,就执行原来的方法 Object proceed = pjp.proceed();//执行原来方法 return proceed; } } private Set<ConstraintViolation> validate(Validator validator,Object object,Class ... groups){ Set constraintViolations = validator.validate(object, groups);//校验不通过的数据会封装到set集合 return constraintViolations; } /** * * @param clz 方法返回值的字节码对象 * @param errorCode 错误码 * @param errorMessage 错误信息 * @param <T> 返回值类型 * @return */ private <T> T getReturnObject(Class<T> clz,String errorCode,String errorMessage) { if(clz.getName().equals("void")){ //没有返回值时 return null; } T t = null; try { t = clz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } setFiledValue("code",errorCode,t); //设置错误码 setFiledValue("desc",errorMessage,t); //设置错误信息 return t; } /** * 调用setXX方法设置属性 * @param fileName * @param value * @param obj */ private void setFiledValue(String fileName,Object value,Object obj) { //该方式找不到对应字段不会报错 if(StringUtils.isEmpty(fileName)){ return; } //转成SetXXX方法名 char[] chars = fileName.toCharArray(); chars[0]=(char) (chars[0]-32); //首字母转大写 //拼接成方法名 String methodName="set"+String.copyValueOf(chars); Class<?> clz = obj.getClass(); Method[] declaredMethods = clz.getDeclaredMethods(); for (Method method : declaredMethods) { String name = method.getName(); boolean equals = method.getName().equals(methodName); if(equals){ try { method.invoke(obj,value); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } }

    //接口调用后结果如下:

  • 相关阅读:
    win10下以管理员身份打开hosts文件
    使用Dockerfile构建镜像命令自己的理解
    我们在删除镜像的时候被告知有容器正在使用,可是容器已经被停止了
    Docker构建镜像过于缓慢解决-----Docker构建服务之部署和备份jekyll网站
    VMware虚拟机ubuntu显示屏幕太小解决办法
    树莓派3B从装系统到安装RYU过程
    树莓派3b 换国内源 更新源
    Raspbain系统无屏幕无网线通过ssh远程连接树莓派设置wifi步骤
    Mplayer 隐藏边框和显示位置的方法
    使用mplayer查看摄像头
  • 原文地址:https://www.cnblogs.com/yangxiaohui227/p/11177817.html
Copyright © 2011-2022 走看看