SpringBoot实现参数校验拦截(采取AOP方式)

打印 上一主题 下一主题

主题 942|帖子 942|积分 2826

一、AOP是什么?

    目的:分离横切关注点(如日记记录、事件管理)与核心业务逻辑。
  优势:提高代码的可读性和可维护性。
  
  关键概念
  

  • 切面(Aspect):包含横切关注点代码的模块。
  • 关照(Advice):切面中的具体动作,比如方法调用之前或之后执行的代码。
  • 毗连点(Join Point):步伐执行的某个具体点,比如方法调用。
  • 切入点(Pointcut):定义在哪些毗连点应用关照。
  
二、使用步骤

1.引入库

代码如下(示例):
  1. <dependencies>
  2.     <!-- 引入SpringBoot Aop依赖 -->
  3.     <dependency>
  4.         <groupId>org.springframework.boot</groupId>
  5.         <artifactId>spring-boot-starter-aop</artifactId>
  6.     </dependency>
  7.     <!-- 引入Aspectj依赖 -->
  8.     <dependency>
  9.         <groupId>org.aspectj</groupId>
  10.         <artifactId>aspectjweaver</artifactId>
  11.         <version>1.9.7</version>
  12.     </dependency>
  13. </dependencies>
复制代码


2.定义注解

   定义注解GlobalInterceptor
  代码示例:如下
  1. @Target({ElementType.METHOD})//注解的目标类型是方法
  2. @Retention(RetentionPolicy.RUNTIME)//注解在运行的时候生效
  3. @Documented
  4. @Mapping
  5. public @interface GlobalInterceptor {
  6.     /**
  7.      * 校验参数
  8.      * @return
  9.      */
  10.     boolean checkParams() default false;
  11. }
复制代码
   定义注解用来校验具体参数
  1. @Retention(RetentionPolicy.RUNTIME)//运行时校验
  2. @Target({ElementType.PARAMETER,ElementType.FIELD})// 指定该注解可以应用的目标类型为参数和字段
  3. public @interface VerifyParam {
  4.     int min() default -1;//校验最小长度
  5.     int max() default -1;//检验最大长度
  6.     boolean required() default false; //校验是否必传
  7.     VerifyRegexEnum regex() default VerifyRegexEnum.NO;//校验正则,默认状态是不校验的
  8. }
复制代码
 可以看到上方的VerifyRegexEnum,这里是一个枚举,重要是来校验参数的,那么枚举代码示例如下:
  1. public enum VerifyRegexEnum {
  2.     NO("","不校验"),
  3.     EMAII("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$","邮箱"),
  4.     PASSWORD("^(?=.*\\d)(?=.*[a-zA-Z])[\\da-zA-Z~!@#$号^&* ]{8,}$","只能是数字,字母,特殊字符 8-18位");
  5.     private String regex;
  6.     private String desc;
  7.     VerifyRegexEnum(String regex, String desc) {
  8.         this.regex = regex;
  9.         this.desc = desc;
  10.     }
  11.     public String getRegex() {
  12.         return regex;
  13.     }
  14.     public String getDesc() {
  15.         return desc;
  16.     }
  17. }
复制代码
 由于这里我的项目中只是简朴的校验了一下邮箱和暗码,需要的话,大家可以自行加入校验方式
 3.定义切面类

  1. @Aspect//表明这是一个切面类
  2. @Component("globalOperatcionAspect")// 交给Spring管理
  3. public class GlobalOperatcionAspect {
  4.     private static final Logger logger = LoggerFactory.getLogger(GlobalOperatcionAspect.class);
  5.     private static final String[] TYPE_BASE = {"java.lang.String","java.lang.Integer","java.lang.Long"};
  6.     //@Pointcut 定义切入点表达式,用于匹配目标方法,此处匹配带有@GlobalInterceptor注解的方法
  7.     @Pointcut("@annotation(com.easypan.annotation.GlobalInterceptor)")
  8.     private void requestInterceptor(){
  9.         // 方法体为空,只是作为一个切入点标识
  10.     }
  11.     //@Before 在目标方法执行前执行
  12.     @Before("requestInterceptor()")
  13.     public void interceptorDo(JoinPoint point) throws BusinessException {
  14.         try {
  15.             Object target = point.getTarget();// 获取目标对象
  16.             Object[] arguments = point.getArgs(); // 获取方法参数
  17.             String methodName = point.getSignature().getName(); // 获取方法名
  18.             Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes(); // 获取方法参数类型
  19.             Method method = target.getClass().getMethod(methodName, parameterTypes); // 获取目标方法
  20.             GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class); // 获取方法上的全局拦截器注解
  21.             if (null == interceptor) { // 如果注解为空则不执行拦截器逻辑
  22.                 return;
  23.             }
  24.             /**
  25.              * 检验参数
  26.              */
  27.             if (interceptor.checkParams()) { // 如果需要检验参数
  28.                 validateParams(method, arguments); // 执行参数校验
  29.             }
  30.         } catch (BusinessException e) {
  31.             logger.error("全局拦截器异常", e); // 记录异常日志
  32.             throw e; // 抛出业务异常
  33.         } catch (Exception e) {
  34.             logger.error("全局拦截器异常", e); // 记录异常日志
  35.             throw new BusinessException(ResponseCodeEnum.CODE_500); // 抛出业务异常
  36.         } catch (Throwable e) {
  37.             logger.error("全局拦截器异常", e); // 记录异常日志
  38.             throw new BusinessException(ResponseCodeEnum.CODE_500); // 抛出业务异常
  39.         }
  40.     }
  41.     /**
  42.      * 检验规则
  43.      * @param method 方法
  44.      * @param arguments 参数列表
  45.      */
  46.     private void validateParams(Method method, Object[] arguments) {
  47.         Parameter[] parameters = method.getParameters(); // 获取方法参数列表
  48.         for (int i = 0; i < parameters.length; i++) { // 遍历参数列表
  49.             Parameter parameter = parameters[i]; // 获取参数
  50.             Object value = arguments[i]; // 获取参数值
  51.             VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class); // 获取参数上的校验注解
  52.             if (verifyParam == null) { // 如果注解为空则跳过
  53.                 continue;
  54.             }
  55.             if (ArrayUtils.contains(TYPE_BASE, parameter.getParameterizedType().getTypeName())) { // 如果是基本类型
  56.                 checkValue(value, verifyParam); // 执行值校验
  57.             } else {
  58.                 checkBObjValue(parameter, value); // 执行对象值校验
  59.             }
  60.         }
  61.     }
  62.     /**
  63.      * 对象值校验
  64.      * @param parameter 参数
  65.      * @param value 参数值
  66.      */
  67.     private void checkBObjValue(Parameter parameter, Object value) {
  68.         try {
  69.             String typeName = parameter.getParameterizedType().getTypeName(); // 获取参数类型名
  70.             Class classz = Class.forName(typeName); // 获取类对象
  71.             Field[] fields = classz.getDeclaredFields(); // 获取类的所有字段
  72.             for (Field field : fields) { // 遍历字段
  73.                 VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class); // 获取字段上的校验注解
  74.                 if (fieldVerifyParam == null) { // 如果注解为空则跳过
  75.                     continue;
  76.                 }
  77.                 field.setAccessible(true); // 设置字段可访问
  78.                 Object resultValue = field.get(value); // 获取字段值
  79.                 checkValue(resultValue, fieldVerifyParam); // 执行值校验
  80.             }
  81.         } catch (BusinessException e) {
  82.             logger.error("校验参数失败", e); // 记录异常日志
  83.             throw e; // 抛出业务异常
  84.         } catch (Exception e) {
  85.             logger.error("校验参数失败", e); // 记录异常日志
  86.             throw new BusinessException(ResponseCodeEnum.CODE_600); // 抛出业务异常
  87.         }
  88.     }
  89.     /**
  90.      * 值校验
  91.      * @param value 值
  92.      * @param verifyParam 校验参数
  93.      */
  94.     private void checkValue(Object value, VerifyParam verifyParam) {
  95.         Boolean isEmpty = value == null || StringTools.isEmpty(value.toString()); // 判断值是否为空
  96.         Integer length = value == null ? 0 : value.toString().length(); // 获取值长度
  97.         /**
  98.          * 检验空
  99.          */
  100.         if (isEmpty && verifyParam.required()) { // 如果值为空且需要校验空
  101.             throw new BusinessException(ResponseCodeEnum.CODE_600); // 抛出业务异常
  102.         }
  103.         /**
  104.          * 检验长度
  105.          */
  106.         if (!isEmpty && (verifyParam.max() != -1 && verifyParam.max() < length) || (verifyParam.min() != -1 && verifyParam.min() > length)) { // 如果值不为空且长度不符合规则
  107.             throw new BusinessException(ResponseCodeEnum.CODE_600); // 抛出业务异常
  108.         }
  109.         /**
  110.          * 校验正则
  111.          */
  112.         if (!isEmpty && !StringTools.isEmpty(verifyParam.regex().getRegex()) && !VerifyUtils.verify(verifyParam.regex(), String.valueOf(value))) { // 如果值不为空且不符合正则规则
  113.             throw new BusinessException(ResponseCodeEnum.CODE_600); // 抛出业务异常
  114.         }
  115.     }
  116. }
复制代码

总结


 去欣赏器直接调用这个路径,没有传参数的话,报错


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表