Springboot 使用自定义注解结合AOP方式校验接口参数

简介: Springboot 使用自定义注解结合AOP方式校验接口参数
aspectjrt
1.8.9
com.alibaba
fastjson
1.2.58
org.springframework.boot
spring-boot-starter-web
新建自定义注解,ParamCheck.java :
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
• @Author : JCccc
• @CreateTime : 2020/5/14
• @Description :
**/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamCheck {
/**
• 是否非空,默认不能为空
*/
boolean notNull() default true;
/**
• 默认值
• @return
*/
String defaultValue() default “”;
}

简单描述:

ElementType.PARAMETER 使用于参数
boolean notNull() default true; 要求参数不为空,默认开启,可以自己传
String defaultValue() default “”; 默认值,默认设置 “”,可以自己传
接下来新建 参数校验的AOP实现类,ParamValidAop.java:
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 java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
• @Author : JCccc
• @CreateTime : 2020/5/14
• @Description :
**/
@Component
@Aspect
public class ParamValidAop {
/**
• 定义有一个切入点,范围为web包下的类
*/
@Pointcut(“execution(public * com.bsapple.vshop.controller….(…))”)
public void checkParam() {
}
@Before(“checkParam()”)
public void doBefore(JoinPoint joinPoint) {
}
/**
• 检查参数是否为空
*/
@Around(“checkParam()”)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = ((MethodSignature) pjp.getSignature());
//得到拦截的方法
Method method = signature.getMethod();
//获取方法参数注解,返回二维数组是因为某些参数可能存在多个注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations == null || parameterAnnotations.length == 0) {
return pjp.proceed();
}
//获取方法参数名
String[] paramNames = signature.getParameterNames();
//获取参数值
Object[] paranValues = pjp.getArgs();
//获取方法参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterAnnotations.length; i++) {
for (int j = 0; j < parameterAnnotations[i].length; j++) {
//如果该参数前面的注解不为空并且是ParamCheck的实例,并且notNull()=true,并且默认值为空,则进行非空校验
if (parameterAnnotations[i][j] != null && parameterAnnotations[i][j] instanceof ParamCheck && ((ParamCheck) parameterAnnotations[i][j]).notNull() && StringUtils.isEmpty(((ParamCheck)parameterAnnotations[i][j]).defaultValue())) {
paramIsNull(paramNames[i], paranValues[i], parameterTypes[i] == null ? null : parameterTypes[i].getName());
break;
}
//如果该参数前面的注解不为空并且是ParamCheck的实例,并且默认值不为空,并且参数值为空,则进行赋默认值
if(parameterAnnotations[i][j] != null && parameterAnnotations[i][j] instanceof ParamCheck && !StringUtils.isEmpty(((ParamCheck)parameterAnnotations[i][j]).defaultValue()) && (paranValues[i] == null || StringUtils.isEmpty(paranValues[i].toString()))){
paranValues[i] = putParam(((ParamCheck)parameterAnnotations[i][j]).defaultValue(), parameterTypes[i]);
}
}
}
return pjp.proceed(paranValues);
}
/**
• 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
• @param joinPoint
*/
@AfterReturning(“checkParam()”)
public void doAfterReturning(JoinPoint joinPoint) {
}
/**
• 参数非空校验,如果参数为空,则抛出ParamIsNullException异常
• @param paramName
• @param value
• @param parameterType
*/
private void paramIsNull(String paramName, Object value, String parameterType) {
if (value == null || “”.equals(value.toString().trim())) {
throw new ParamIsNullException(paramName, parameterType,“参数为空”);
}
}
private Object putParam(Object value, Class<?> parameterType) {
return CastValueTypeUtil.parseValue(parameterType, value.toString());
}
}

需要注意,这个路径是你准备添加校验的controller的路径,改成你自己的:

然后是校验参数里面使用到的参数转换工具类,CastValueTypeUtil.java:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
*转换object类型
**/
public class CastValueTypeUtil {
public static Object parseValue(Class<?> parameterTypes, String value) {
if(value==null || value.trim().length()==0){
return null;
}
value = value.trim();
if (Byte.class.equals(parameterTypes) || Byte.TYPE.equals(parameterTypes)) {
return parseByte(value);
} else if (Boolean.class.equals(parameterTypes) || Boolean.TYPE.equals(parameterTypes)) {
return parseBoolean(value);
}/* else if (Character.class.equals(fieldType) || Character.TYPE.equals(fieldType)) {
return value.toCharArray()[0];
}*/ else if (String.class.equals(parameterTypes)) {
return value;
} else if (Short.class.equals(parameterTypes) || Short.TYPE.equals(parameterTypes)) {
return parseShort(value);
} else if (Integer.class.equals(parameterTypes) || Integer.TYPE.equals(parameterTypes)) {
return parseInt(value);
} else if (Long.class.equals(parameterTypes) || Long.TYPE.equals(parameterTypes)) {
return parseLong(value);
} else if (Float.class.equals(parameterTypes) || Float.TYPE.equals(parameterTypes)) {
return parseFloat(value);
} else if (Double.class.equals(parameterTypes) || Double.TYPE.equals(parameterTypes)) {
return parseDouble(value);
} else if (Date.class.equals(parameterTypes)) {
return parseDate(value);
} else {
throw new RuntimeException(“request illeagal type, type must be Integer not int Long not long etc, type=” + parameterTypes);
}
}
public static Byte parseByte(String value) {
try {
value = value.replaceAll(" ", “”);
return Byte.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseByte but input illegal input=” + value, e);
}
}
public static Boolean parseBoolean(String value) {
value = value.replaceAll(" ", “”);
if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
return Boolean.TRUE;
} else if (Boolean.FALSE.toString().equalsIgnoreCase(value)) {
return Boolean.FALSE;
} else {
throw new RuntimeException(“parseBoolean but input illegal input=” + value);
}
}
public static Integer parseInt(String value) {
try {
value = value.replaceAll(" ", “”);
return Integer.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseInt but input illegal input=” + value, e);
}
}
public static Short parseShort(String value) {
try {
value = value.replaceAll(" ", “”);
return Short.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseShort but input illegal input=” + value, e);
}
}
public static Long parseLong(String value) {
try {
value = value.replaceAll(" ", “”);
return Long.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseLong but input illegal input=” + value, e);
}
}
public static Float parseFloat(String value) {
try {
value = value.replaceAll(" ", “”);
return Float.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseFloat but input illegal input=” + value, e);
}
}
public static Double parseDouble(String value) {
try {
value = value.replaceAll(" ", “”);
return Double.valueOf(value);
} catch(NumberFormatException e) {
throw new RuntimeException(“parseDouble but input illegal input=” + value, e);
}
}
public static Date parseDate(String value) {
try {
String datePattern = “yyyy-MM-dd HH:mm:ss”;
SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
return dateFormat.parse(value);
} catch(ParseException e) {
throw new RuntimeException(“parseDate but input illegal input=” + value, e);
}
}
}
然后是新建一个自定义异常,专门用于校验参数为空的时候抛出,ParamIsNullException.java:
/**
• @Author : JCccc
• @CreateTime : 2020/5/14
• @Description :
**/
public class ParamIsNullException extends RuntimeException {
private final String parameterName;
private final String parameterType;
private final String message;
public ParamIsNullException(String parameterName, String parameterType, String message) {
super();
this.parameterName = parameterName;
this.parameterType = parameterType;
this.message = message;
}
@Override
public String getMessage() {
return “请求参数类型:” + this.parameterType + “,参数名: '” + this.parameterName + message;
}
public final String getParameterName() {
return this.parameterName;
}
public final String getParameterType() {
return this.parameterType;
}
}

到这里,其实可以看到自定义注解以及AOP已经实现完毕。

接下来是做一下,统一的返回参以及全局异常捕获,

新建接口BaseErrorInfoInterface.java:

/**
• @Author:JCccc
• @Description:此接口用于返回码枚举使用
• @Date: created in 15:11 2019/5/3
*/
public interface BaseErrorInfoInterface {
/** 错误码*/
String getResultCode();
/** 错误描述*/
String getResultMsg();
}
返回码的枚举类,CommonEnum.java:
/**
• @Author:JCccc
• @Description:
• @Date: created in 15:13 2019/5/3
*/
public enum CommonEnum implements BaseErrorInfoInterface {
// 数据操作错误定义
SUCCESS(“200”, “成功!”),
BODY_NOT_MATCH(“400”, “请求的数据格式不符!”),
SIGNATURE_NOT_MATCH(“401”, “请求的数字签名不匹配!”),
NOT_FOUND(“404”, “未找到该资源!”),
INTERNAL_SERVER_ERROR(“500”, “服务器内部错误!”),
SERVER_BUSY(“503”, “服务器正忙,请稍后再试!”),
REQUEST_METHOD_SUPPORT_ERROR(“40001”,“当前请求方法不支持”);
/**
• 错误码
*/
private String resultCode;
/**
• 错误描述
*/
private String resultMsg;
CommonEnum(String resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
@Override
public String getResultCode() {
return resultCode;
}
@Override
public String getResultMsg() {
return resultMsg;
}
}
然后是一个简单的返回体专用类,ResultBody.java:
相关文章
|
1月前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
1天前
|
监控 Java Spring
自定义注解+AOP切面日志+源码
自定义注解+AOP切面日志+源码
10 1
|
9天前
|
Java Spring
自定义注解+AOP
自定义注解+AOP
14 1
|
1月前
|
安全
自定义注解,aop实现注解锁
自定义注解,aop实现注解锁
|
1月前
|
Java 测试技术 开发者
【亮剑】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
|
1月前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
29 0
|
1月前
AOP&面向切面编程
AOP&面向切面编程
61 0
|
1月前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
|
1月前
|
数据库
AOP(面向切面编程)的基本概念和原理
AOP(面向切面编程)的基本概念和原理
153 0
|
1月前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
29 1