spring boot2使用AOP注解和反射知识实现非空参数的切面判断

项目demo  GitHub上 https://github.com/zhang-xiaoxiang/judge-parameter

我求求你们不要再黑蔡徐坤了好吗?他是我朋友的救命恩人啊,上次我同学出了车祸,成了植物人。医生都放弃了,都说别救了。可那天在病房,电视上在播蔡徐坤打篮球的视频,我朋友硬是从昏迷中醒来把电视给关了!!!

你还在为一大堆参数判断而烦恼吗?这里提供一种思路,使用AOP切面编程的实现参数非空判断

案例,代码中是不是经常出现以下逻辑

 你是否想达到如下效果(点击放大好看一点)

 比如参数不完整

 

参数完整,正常添加

我的代码结构

 

解决思路如下

先导入重要相关的POM依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--aop切面-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <!--lang3-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>

配置Aop

//package com.example.judgeparameter.aop;


import com.example.judgeparameter.result.ResponseData;
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * SetPropertyName:非空参数切面类
 *
 * @author zhangxiaoxiang
 * @date: 2019/05/20
 */
@Component
@Aspect
public class AopParameter {
    private static final Logger logger = LoggerFactory.getLogger(AopParameter.class);
    static final String split = ",";

    @Pointcut("@annotation(com.example.judgeparameter.aop.RequestRequire)")
    public void controllerInteceptor() {
    }

    /**
     * controller层增强类,用于检测参数为空的情况,低侵入式,不影响已经写好的代码
     *
     * @return java.lang.Object
     * @throws
     * @params pjp
     **/
    @Around("controllerInteceptor()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //参数非空返回给前端的
        ResponseData<Object> responseData = new ResponseData<>();
        Map map = new HashMap();

        // 获取注解的 方法参数列表
        Object[] args = pjp.getArgs();

        // 获取被注解的 方法
        MethodInvocationProceedingJoinPoint mjp = (MethodInvocationProceedingJoinPoint) pjp;
        MethodSignature signature = (MethodSignature) mjp.getSignature();
        Method method = signature.getMethod();

        // 获取方法上的 注解
        RequestRequire require = method.getAnnotation(RequestRequire.class);


        // 以防万一,将中文的逗号替换成英文的逗号,并且把空格去掉了
        String fieldNames = require.require().replace(",", ",").replace(" ", "");
        //logger.info("fieldNames==>" + fieldNames);

        // 从参数列表中获取参数对象 就是注解如parameter = LoginUser.class的类
        Object parameter = null;
        for (Object pa : args) {
            //class相等表示是同一个对象
            if (pa.getClass() == require.parameter()) {
                parameter = pa;
                // logger.info("从参数列表中获取参数对象:" + parameter.toString());
            }
        }

        // 通过反射去和指定的属性值判断是否非空
        // 获得参数的class 就是待判断参数的类
        Class aClass = parameter.getClass();


        // 遍历参数,找到是否为空
        for (String name : fieldNames.split(split)) {
            //需要判断接口参数是否和请求的对象属性一致,所以异常包裹一下
            Field declaredField;
            try {
                //反射获取属性
                declaredField = aClass.getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                responseData.setCode(500);
                responseData.setMsg("后台接口非空参数错误:" + name);
                e.printStackTrace();
                return responseData;
            } catch (SecurityException e) {
                responseData.setCode(500);
                responseData.setMsg("SecurityException:" + name);
                e.printStackTrace();
                return responseData;
            }
            String fieldName = declaredField.getName();
            //反射的常规操作
            declaredField.setAccessible(true);
            //获取属性
            Object fieldObject = declaredField.get(parameter);
            // 获取属性的中文名称
            SetPropertyName spv = declaredField.getAnnotation(SetPropertyName.class);
            //就是判断RequestRequire注解里面的参数,如果不为空就赋值
            if (spv != null && StringUtils.isNotBlank(spv.value())) {
                fieldName = spv.value();
            }
            //如果属性为空就放入map,跳出循环后并给返回对象ResultVO<Object>
            if (fieldObject == null) {
                map.put(fieldName, "该参数不能为空!");
                responseData.setData(map);
                continue;

            }

            // 如果type是类类型,则前面包含"class ",后面跟类名
            if (declaredField.getGenericType().toString().equals("class java.lang.String")) {
                if (StringUtils.isBlank((String) fieldObject)) {
                    map.put(fieldName, "该参数不能为空!");
                    responseData.setData(map);
                    continue;
                }
            }
            // 如果是数字类型的---留着拓展的(时间类型,文件类型)
            if (declaredField.getGenericType().toString().equals("class java.lang.Integer")
                    || declaredField.getGenericType().toString().equals("class java.lang.Long")
                    || declaredField.getGenericType().toString().equals("class java.lang.Double")
                    || declaredField.getGenericType().toString().equals("class java.lang.Float")) {
                if (fieldObject == null) {
                    map.put(fieldName, "该参数不能为空!");
                    responseData.setData(map);
                }

            }

        }


        if (responseData != null && responseData.getData() != null) {
            responseData.setCode(500);
            responseData.setMsg("请求的参数为空或者拼写错误,导致后台无法正常接收(也可能后台没有采用json格式接收),返回详情见data!");
            //data在循环的时候已经放进了map了,所以这里其实data有数据的
            return responseData;
        }
        // 如果没有报错,放行
        return pjp.proceed();
    }

}

 使用2个注解

//package com.example.judgeparameter.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * RequestRequire:这个注解让aop来统一的帮我们进行非空的判断。
 *
 * @author zhangxiaoxiang
 * @date: 2019/05/20
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface RequestRequire {

    /**
     * 请求当前接口所需要的参数,多个以小写的逗号隔开,可以使用中文,做了处理的哈,也可以有空格
     *
     * @return
     */
    String require() default "";


    /**
     * 传递参数的对象类型
     */
    Class<?> parameter() default Object.class;

}
//package com.example.judgeparameter.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * SetPropertyName:这个注解的主要目的是为了非空检查返回属性的中文信息
 * @author zhangxiaoxiang
 * @date: 2019/05/20
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SetPropertyName {

    String value() default "";
}

 最后就是dao,service,controller三层的基本骨架和自定义异常和返回json格式的一些类或者接口,就不在这里详细粘贴代码了,毕竟提供了GitHub地址

这里留一个思考:如何判断属相的属性为空呢?比如user的有个属性是Car,car又有属性,怎么判断car的属性是否为空?当然看了我的demo希望各位大佬也指点一二,目前我就是按照这个demo实现的参数判断,至于参数是对象,那种复杂的参数判断,目前还是使用的老办法,if else,实际开发这样也或多或少的免得AOP判断带来的一些小弊端

发布了57 篇原创文章 · 获赞 33 · 访问量 814万+

猜你喜欢

转载自blog.csdn.net/wozniakzhang/article/details/96912106
今日推荐