AOP + 注解 实现通用的接口参数校验
【摘要】 写移动端接口的时候,为了校验参数,传统的做法是加各种判断,写了很多重复的代码,而且也不美观。为了增加代码复用性,美观的校验参数,采用AOP + 注解的方式来实现接口的参数校验(使用拦截器也可以实现),在需要校验参数的方法上加上自定义的注解即可。
写移动端接口的时候,为了校验参数,传统的做法是加各种判断,写了很多重复的代码,而且也不美观。为了增加代码复用性,美观的校验参数,采用AOP + 注解的方式来实现接口的参数校验(使用拦截器也可以实现),在需要校验参数的方法上加上自定义的注解即可。
代码文件目录
代码实现
自定义异常:RRException
/**
* @description
*/
public class RRException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public RRException(String msg) {
super(msg);
this.msg = msg;
}
public RRException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public RRException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public RRException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
全局异常处理器:RRExceptionHandler
/**
* @description
*/
@RestControllerAdvice
public class RRExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@ExceptionHandler(RRException.class)
public R handleRRException(RRException e){
R r = new R();
r.put("code", e.getCode());
r.put("msg", e.getMessage());
return r;
}
}
响应数据封装:R
* @description
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
put("msg", "success");
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R ok(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok(List<Object> list) {
R r = new R();
r.put("msg", list);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
注解:ParamCheck
/**
* @description
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface ParamCheck {
//字段校验规则,格式:字段名+校验规则+冒号+错误信息,例如:name<11:名字必须少于11位
String[] value();
}
工具类:CheckUtil
* @description
*/
@Component
public class CheckUtil {
private static final Logger logger = LoggerFactory.getLogger(CheckUtil.class);
private static final String SEPARATOR = ":";
public String doCheck(ProceedingJoinPoint point) {
// 获取方法参数值
Object[] arguments =point.getArgs();
// 获取方法
Method method = getMethod(point);
// 默认的错误信息
String methodInfo = StringUtils.isEmpty(method.getName()) ? "" : "while calling " + method.getName();
String msg = "";
if (isCheck(method,arguments)) {
ParamCheck annotation = method.getAnnotation(ParamCheck.class);
String[] fields = annotation.value();
// 只支持对第一个参数进行校验
Object vo = arguments[0];
if (vo == null) {
msg = "param can not be null";
} else {
for (String field : fields) {
// 解析字段
FieldInfo info = resolveField(field, methodInfo);
// 获取字段的值
Object value = ReflectionUtil.invokeGetter(vo, info.getField());
// 执行校验规则
Boolean isValid = info.getOptEnum().fun.apply(value, info.getOperatorNum());
msg = isValid ? msg : info.getPromptMsg();
}
}
}
return msg;
}
/**
* 判断是否符合参数规则
* @param method 方法
* @param arguments 方法参数
* @return 是否符合
*/
private Boolean isCheck(Method method,Object[] arguments) {
Boolean isCheck = Boolean.TRUE;
if (!method.isAnnotationPresent(ParamCheck.class) || arguments == null || arguments.length != 1) {
isCheck = Boolean.FALSE;
}
return isCheck;
}
/**
* 获取方法
* @param point ProceedingJoinPoint
* @return 方法
*/
private Method getMethod(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method = point
.getTarget()
.getClass()
.getDeclaredMethod(point.getSignature().getName(),
method.getParameterTypes());
} catch (SecurityException | NoSuchMethodException e) {
logger.error("" + e);
}
}
return method;
}
/**
* 解析字段
* @param fieldStr 字段字符串
* @param methodInfo 方法信息
* @return 字段信息实体类
*/
private FieldInfo resolveField(String fieldStr, String methodInfo) {
FieldInfo fieldInfo = new FieldInfo();
String innerMsg = "";
// 解析提示信息
if (fieldStr.contains(SEPARATOR)) {
innerMsg = fieldStr.split(SEPARATOR)[1];
fieldStr = fieldStr.split(SEPARATOR)[0];
}
// 解析操作符
if (fieldStr.contains(OperatorEnum.GREATER_THAN_EQUAL.value)) {
fieldInfo.setOptEnum(OperatorEnum.GREATER_THAN_EQUAL);
} else if (fieldStr.contains(OperatorEnum.LESS_THAN_EQUAL.value)) {
fieldInfo.setOptEnum(OperatorEnum.LESS_THAN_EQUAL);
} else if (fieldStr.contains(OperatorEnum.GREATER_THAN.value)) {
fieldInfo.setOptEnum(OperatorEnum.GREATER_THAN);
} else if (fieldStr.contains(OperatorEnum.LESS_THAN.value)) {
fieldInfo.setOptEnum(OperatorEnum.LESS_THAN);
} else if (fieldStr.contains(OperatorEnum.NOT_EQUAL.value)) {
fieldInfo.setOptEnum(OperatorEnum.NOT_EQUAL);
} else {
fieldInfo.setOptEnum(OperatorEnum.NOT_NULL);
}
// 不等于空,直接赋值字段
OperatorEnum operatorEnum = fieldInfo.getOptEnum();
if (operatorEnum == OperatorEnum.NOT_NULL) {
fieldInfo.setField(fieldStr);
fieldInfo.setOperatorNum("");
}
// 其他操作符,需要分离出字段和操作数
else {
fieldInfo.setField(fieldStr.split(operatorEnum.value)[0]);
fieldInfo.setOperatorNum(fieldStr.split(operatorEnum.value)[1]);
}
fieldInfo.setOperator(operatorEnum.value);
// 处理错误信息
String defaultMsg = fieldInfo.getField() + " must " + fieldInfo.getOperator() + " " + fieldInfo.getOperatorNum() + methodInfo;
fieldInfo.setPromptMsg(StringUtils.isEmpty(innerMsg) ? defaultMsg : innerMsg);
return fieldInfo;
}
}
工具类:ReflectionUtil
* @description
*/
public class ReflectionUtil {
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
private static final String CGLIB_CLASS_SEPARATOR = "$$";
private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);
/**
* 调用Getter方法.
*/
public static Object invokeGetter(Object obj, String propertyName) {
String getterMethodName = GETTER_PREFIX
+ StringUtils.capitalize(propertyName);
return invokeMethod(obj, getterMethodName, new Class[]{},
new Object[]{});
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. 同时匹配方法名+参数类型,
*/
public static Object invokeMethod(final Object obj,
final String methodName, final Class<?>[] parameterTypes,
final Object[] args) {
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("Could not find method ["
+ methodName + "] on target [" + obj + "]");
}
try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
* <p>
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object...
* args)
*/
public static Method getAccessibleMethod(final Object obj,
final String methodName, final Class<?>... parameterTypes) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType
.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName,
parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
}
}
return null;
}
/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier
.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}
/**
* 将反射时的checked exception转换为unchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(
Exception e) {
if ((e instanceof IllegalAccessException)
|| (e instanceof IllegalArgumentException)
|| (e instanceof NoSuchMethodException)) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(
((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException("Unexpected Checked Exception.", e);
}
}
枚举:OperatorEnum
/**
* @description 操作枚举,封装操作符和对应的校验规则
*/
public enum OperatorEnum {
/**
* 大于
*/
GREATER_THAN(">", DiffTypeParamCheck::isGreaterThan),
/**
* 大于等于
*/
GREATER_THAN_EQUAL(">=", DiffTypeParamCheck::isGreaterThanEqual),
/**
* 小于
*/
LESS_THAN("<", DiffTypeParamCheck::isLessThan),
/**
* 小于等于
*/
LESS_THAN_EQUAL("<=", DiffTypeParamCheck::isLessThanEqual),
/**
* 不等于
*/
NOT_EQUAL("!=", DiffTypeParamCheck::isNotEqual),
/**
* 不为空
*/
NOT_NULL("not null", DiffTypeParamCheck::isNotNull);
public String value;
public BiFunction<Object,String,Boolean> fun;
OperatorEnum(String value, BiFunction<Object,String,Boolean> fun) {
this.value = value;
this.fun = fun;
}
}
字段信息:FieldInfo
* @description
*/
@Data
public class FieldInfo {
/**
* 字段
*/
private String field;
/**
* 提示信息
*/
private String promptMsg;
/**
* 操作符
*/
private String operator;
/**
* 操作数
*/
private String operatorNum;
/**
* 操作枚举
*/
private OperatorEnum optEnum;
}
对不同类型的值进行校验:DiffTypeParamCheck
* @description 对不同类型的值进行校验
*/
public class DiffTypeParamCheck {
/**
* 是否不为空
* @param value 字段值
* @param operatorNum 操作数,这里不需要,只是为了参数统一
* @return 是否不为空
*/
public static Boolean isNotNull(Object value, String operatorNum) {
Boolean isNotNull = Boolean.TRUE;
Boolean isStringNull = (value instanceof String) && StringUtils.isEmpty((String) value);
Boolean isCollectionNull = (value instanceof Collection) && CollectionUtils.isEmpty((Collection) value);
if (value == null) {
isNotNull = Boolean.FALSE;
} else if (isStringNull || isCollectionNull) {
isNotNull = Boolean.FALSE;
}
return isNotNull;
}
/**
* 是否大于
* @param value 字段值
* @param operatorNum 操作数
* @return 是否大于
*/
public static Boolean isGreaterThan(Object value, String operatorNum) {
Boolean isGreaterThan = Boolean.FALSE;
if (value == null) {
return Boolean.FALSE;
}
Boolean isStringGreaterThen = (value instanceof String) && ((String) value).length() > Integer.valueOf(operatorNum);
Boolean isLongGreaterThen = (value instanceof Long) && ((Long) value) > Long.valueOf(operatorNum);
Boolean isIntegerGreaterThen = (value instanceof Integer) && ((Integer) value) > Integer.valueOf(operatorNum);
Boolean isShortGreaterThen = (value instanceof Short) && ((Short) value) > Short.valueOf(operatorNum);
Boolean isFloatGreaterThen = (value instanceof Float) && ((Float) value) > Float.valueOf(operatorNum);
Boolean isDoubleGreaterThen = (value instanceof Double) && ((Double) value) > Double.valueOf(operatorNum);
Boolean isCollectionGreaterThen = (value instanceof Collection) && ((Collection) value).size() > Integer.valueOf(operatorNum);
if (isStringGreaterThen || isLongGreaterThen || isIntegerGreaterThen ||
isShortGreaterThen || isFloatGreaterThen || isDoubleGreaterThen || isCollectionGreaterThen) {
isGreaterThan = Boolean.TRUE;
}
return isGreaterThan;
}
/**
* 是否大于等于
* @param value 字段值
* @param operatorNum 操作数
* @return 是否大于等于
*/
public static Boolean isGreaterThanEqual(Object value, String operatorNum) {
Boolean isGreaterThanEqual = Boolean.FALSE;
if (value == null) {
return Boolean.FALSE;
}
Boolean isStringGreaterThenEqual = (value instanceof String) && ((String) value).length() >= Integer.valueOf(operatorNum);
Boolean isLongGreaterThenEqual = (value instanceof Long) && ((Long) value) >= Long.valueOf(operatorNum);
Boolean isIntegerGreaterThenEqual = (value instanceof Integer) && ((Integer) value) >= Integer.valueOf(operatorNum);
Boolean isShortGreaterThenEqual = (value instanceof Short) && ((Short) value) >= Short.valueOf(operatorNum);
Boolean isFloatGreaterThenEqual = (value instanceof Float) && ((Float) value) >= Float.valueOf(operatorNum);
Boolean isDoubleGreaterThenEqual = (value instanceof Double) && ((Double) value) >= Double.valueOf(operatorNum);
Boolean isCollectionGreaterThenEqual = (value instanceof Collection) && ((Collection) value).size() >= Integer.valueOf(operatorNum);
if (isStringGreaterThenEqual || isLongGreaterThenEqual || isIntegerGreaterThenEqual ||
isShortGreaterThenEqual || isFloatGreaterThenEqual || isDoubleGreaterThenEqual || isCollectionGreaterThenEqual) {
isGreaterThanEqual = Boolean.TRUE;
}
return isGreaterThanEqual;
}
/**
* 是否少于
* @param value 字段值
* @param operatorNum 操作数
* @return 是否少于
*/
public static Boolean isLessThan(Object value, String operatorNum) {
Boolean isLessThan = Boolean.FALSE;
if (value == null) {
return Boolean.FALSE;
}
Boolean isStringLessThen = (value instanceof String) && ((String) value).length() < Integer.valueOf(operatorNum);
Boolean isLongLessThen = (value instanceof Long) && ((Long) value) < Long.valueOf(operatorNum);
Boolean isIntegerLessThen = (value instanceof Integer) && ((Integer) value) < Integer.valueOf(operatorNum);
Boolean isShortLessThen = (value instanceof Short) && ((Short) value) < Short.valueOf(operatorNum);
Boolean isFloatLessThen = (value instanceof Float) && ((Float) value) < Float.valueOf(operatorNum);
Boolean isDoubleLessThen = (value instanceof Double) && ((Double) value) < Double.valueOf(operatorNum);
Boolean isCollectionLessThen = (value instanceof Collection) && ((Collection) value).size() < Integer.valueOf(operatorNum);
if (isStringLessThen || isLongLessThen || isIntegerLessThen ||
isShortLessThen || isFloatLessThen || isDoubleLessThen || isCollectionLessThen) {
isLessThan = Boolean.TRUE;
}
return isLessThan;
}
/**
* 是否少于等于
* @param value 字段值
* @param operatorNum 操作数
* @return 是否少于等于
*/
public static Boolean isLessThanEqual(Object value, String operatorNum) {
Boolean isLessThanEqual = Boolean.FALSE;
if (value == null) {
return Boolean.FALSE;
}
Boolean isStringLessThenEqual = (value instanceof String) && ((String) value).length() <= Integer.valueOf(operatorNum);
Boolean isLongLessThenEqual = (value instanceof Long) && ((Long) value) <= Long.valueOf(operatorNum);
Boolean isIntegerLessThenEqual = (value instanceof Integer) && ((Integer) value) <= Integer.valueOf(operatorNum);
Boolean isShortLessThenEqual = (value instanceof Short) && ((Short) value) <= Short.valueOf(operatorNum);
Boolean isFloatLessThenEqual = (value instanceof Float) && ((Float) value) <= Float.valueOf(operatorNum);
Boolean isDoubleLessThenEqual = (value instanceof Double) && ((Double) value) <= Double.valueOf(operatorNum);
Boolean isCollectionLessThenEqual = (value instanceof Collection) && ((Collection) value).size() <= Integer.valueOf(operatorNum);
if (isStringLessThenEqual || isLongLessThenEqual || isIntegerLessThenEqual ||
isShortLessThenEqual || isFloatLessThenEqual || isDoubleLessThenEqual || isCollectionLessThenEqual) {
isLessThanEqual = Boolean.TRUE;
}
return isLessThanEqual;
}
/**
* 是否不等于
* @param value 字段值
* @param operatorNum 操作数
* @return 是否不等于
*/
public static Boolean isNotEqual(Object value, String operatorNum) {
Boolean isNotEqual = Boolean.FALSE;
if (value == null) {
return Boolean.FALSE;
}
Boolean isStringNotEqual = (value instanceof String) && !value.equals(operatorNum);
Boolean isLongNotEqual = (value instanceof Long) && !value.equals(Long.valueOf(operatorNum));
Boolean isIntegerNotEqual = (value instanceof Integer) && !value.equals(Integer.valueOf(operatorNum));
Boolean isShortNotEqual = (value instanceof Short) && !value.equals(Short.valueOf(operatorNum));
Boolean isFloatNotEqual = (value instanceof Float) && !value.equals(Float.valueOf(operatorNum));
Boolean isDoubleNotEqual = (value instanceof Double) && !value.equals(Double.valueOf(operatorNum));
Boolean isCollectionNotEqual = (value instanceof Collection) && ((Collection) value).size() != Integer.valueOf(operatorNum);
if (isStringNotEqual || isLongNotEqual || isIntegerNotEqual ||
isShortNotEqual || isFloatNotEqual || isDoubleNotEqual || isCollectionNotEqual) {
isNotEqual = Boolean.TRUE;
}
return isNotEqual;
}
切面类:ParamCheckAspect
* @description
*/
@Aspect
@Component
public class ParamCheckAspect {
private static final Logger logger = LoggerFactory.getLogger(ParamCheckAspect.class);
@Autowired
private CheckUtil checkUtil;
@Around(value = "@annotation(com.smartMap.media.common.paramcheck.annotation.ParamCheck)")
public Object check(ProceedingJoinPoint point) throws Throwable {
Object obj;
// 参数校验
String msg = checkUtil.doCheck(point);
if (!StringUtils.isEmpty(msg)) {
throw new RRException(msg, 400);
}
// 通过校验,继续执行原有方法
obj = point.proceed();
return obj;
}
}
测试验证
参数实体类:SelectorObj
* @description
*/
@Data
public class SelectorObj {
private String value;
private String label;
}
控制器:TestController
/**
* @description
*/
@RestController
@RequestMapping("/mobile/test")
public class TestController {
@ParamCheck({"value:value 不能为空","label!=123:label 不能为123"})
@RequestMapping("testParamCheck")
public R testParamCheck(SelectorObj obj) {
System.out.println(obj);
return R.ok().put("obj",obj);
}
}
结果:
1、非空检验
2、非特定值校验
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)