前言:
本来项目是Springboot的,用的是 validation 对接口传入的参数进行判空处理,自己测试没得问题(测试服务器上也没得问题),但是技术经理最后需要打成war包(springboot多为jar包)部署到Tomcat上,但是发现打包为war包之后validation就突然失效了,略微查找之后没得找到问题根源,当时由于时间紧张,@RequestParam不能满足需求,就自己写了自定义的注解通过切面暂时实现,感觉挺有意思,就记录一下:直接上代码
一. 判断入参不为空的注解
1.1 : 定义注解 StringNullRegex (名字自己随意取)
import java.lang.annotation.*;
/**
* @Description 判断接口入参不能为空的自定义注解
* @AuThor 江
*/
@Target(ElementType.METHOD) // 作用于方法
@Retention(RetentionPolicy.RUNTIME)
public @interface StringNullRegex {
}
1.2 : 注解功能实现 StringNullRegexAspect
import com.example.waitdemo.utils.HttpResult;
import com.example.waitdemo.utils.StringNullUtil;
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.lang.reflect.Parameter;
import java.util.*;
/**
* @Description StringNullRegex注解的实现
* @ClassName StringNullRegexAspect
*/
@Aspect // 切面
@Component // 表示这是一个bean,由Spring进行管理 或者说输入spring
public class StringNullRegexAspect {
// 配置织入点 @annotation 为自定义的注解的位置(路径)
@Pointcut("@annotation(com.example.waitdemo.config.StringNullRegex)")
public void annotationPointcut() {
}
/**
*
*/
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 通过方法获取参数及其内容的 map
Map<String, String> parameter = getParameter(method, joinPoint.getArgs());
// 对参数进行判空处理
for (String key : parameter.keySet()) {
if (StringNullUtil.isEmpty(parameter.get(key))){
return HttpResult.newError(key + "参数不能为空!");
}
}
return joinPoint.proceed();
}
// 获取参数及对应数据
private Map<String, String> getParameter(Method method, Object[] args) {
Map<String, String> argList = new HashMap<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
argList.put(parameters[i].getName(), String.valueOf(args[i]));
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList;
} else {
return argList;
}
}
}
二. 判断入参为对象 不为空的注解
2.1 : 定义注解 NotNotNull (名字自己随意取,这里是为了和 NotNull 区分开)
/**
* @Description 配合 ClassNotNull 进行实体类验证
* 作用于实体类的字段上边
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) // 作用于字段
public @interface NotNotNull {
String message() default "";
}
2.2 : 定义注解 ClassNotNull
/**
* @Description 配合 NotNotNull 实体类验证
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassNotNull {
}
2.3 : 注解功能实现 ClassNotNull
import com.example.waitdemo.utils.HttpResult;
import com.example.waitdemo.utils.StringNullUtil;
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.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* @Description
* @ClassName ClassNotNullAspect
*/
@Aspect // 切面
@Component // 表示这是一个bean,由Spring进行管理 或者说输入spring
public class ClassNotNullAspect {
// 配置织入点
@Pointcut("@annotation(com.example.waitdemo.config.ClassNotNull)")
public void ClassNotNullPointcut() {
}
@Around("ClassNotNullPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
//System.out.println("---------");
Object arg = joinPoint.getArgs()[0];
Parameter[] parameters = method.getParameters();
//获取class对象
Class<?> aClass = parameters[0].getType();
//获取当前对象所有属性 使用带Declared的方法可访问private属性
Field[] declaredFields = aClass.getDeclaredFields();
for(Field field:declaredFields){
//开启访问权限
field.setAccessible(true);
//使用此方法 field.get(Object obj) 可以获取 当前对象这个列的值
Object o = field.get(arg);
Annotation annotation = field.getDeclaredAnnotation(NotNotNull.class);
//如果没有设置当前注解 不用校验
if(annotation == null){
continue;
}
//如果设置了当前注解,但是没有值,抛出异常
if(StringNullUtil.isEmpty(String.valueOf(o))){
//获取注解接口对象
NotNotNull notNull = (NotNotNull)annotation;
if(StringNullUtil.isNotEmpty(notNull.message())){
System.out.println("notNull.message() = " + notNull.message());
//设置了注解message值 直接返回
// 这里又两种返回方法 ,
// 1: 通过自定义错误,然后进行全局拦截
//throw new ClassNotNullException(notNull.message());
// 2: 自己包装的返回参数 (通过枚举等)
return HttpResult.newError(notNull.message());
}else{
System.out.println("field.getName() = " + field.getName()+" is null");
//没有设置可以拼接
//throw new ClassNotNullException(field.getName()+" is null");
return HttpResult.newError(field.getName()+" is null");
}
}
}
return joinPoint.proceed();
}
}
自定义的注解已经完成,准备使用
三. 定义用户类:
自定义注解 NotNotNull 的使用位置
import com.example.waitdemo.config.NotNotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* TODO
*
* @ClassName User
* @Author
* @Date
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class UserProperty {
@NotNotNull(message = "名字不能为空!")
private String name;
private Integer age;
@NotNotNull(message = "qq is null")
private Integer qq;
}
四. controller 中使用自定义注解
/**
* @ClassName WaitController
*/
@Log4j2
@RestController
@RequestMapping("/wait")
public class WaitController {
@RequestMapping(value = "/test_string_null",method = RequestMethod.GET)
@StringNullRegex
public HttpResult testStringNullRegex(String name, Integer age){
System.out.println("名字 = " + name);
System.out.println("年龄 = " + age);
return HttpResult.newSuccess();
}
@RequestMapping(value = "/testTwo",method = RequestMethod.GET)
@ClassNotNull
public HttpResult testTwo(UserProperty userProperty){
String name = userProperty.getName();
System.out.println("名字 = " + name);
Integer age = userProperty.getAge();
System.out.println("年龄 = " + age);
Integer qq = userProperty.getQq();
System.out.println("QQ = " + qq);
return HttpResult.newSuccess();
}
}
其他问题:
到这里基本的功能实现了,但是新问题:
- 不支持 多参数中可以为空,可以不为空的问题
解释: 这个没有用到就没得测试,对象注解中算实现了,但是不太合格,通过自定义注解应该也可以实现,感兴趣的可以试一下^_^