项目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判断带来的一些小弊端