Spring AOP实战:如何优雅地获取请求参数并实现统一处理

1次阅读
没有评论

共计 2267 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

为什么需要统一处理请求参数?

在日常 Web 开发中,我们经常遇到这些场景:

Spring AOP 实战:如何优雅地获取请求参数并实现统一处理

  1. 需要对所有接口的入参进行非空校验或格式校验
  2. 要记录关键接口的请求参数日志
  3. 某些敏感接口需要做参数解密
  4. 需要统计接口调用时传递的特定参数

如果每个接口都手动实现这些逻辑,会导致大量重复代码,维护困难。而 AOP 可以完美解决这个问题。

AOP vs 拦截器:如何选择?

  • 拦截器 (Interceptor):更适合处理与 HTTP 协议直接相关的操作,如:
  • 请求头处理
  • 跨域设置
  • 全局的权限检查
  • 请求 / 响应数据转换

  • AOP:更适合业务层面的统一处理,如:

  • 方法级的参数校验
  • 业务日志记录
  • 特定注解的解析
  • 方法执行时间监控

选择 AOP 的主要原因是它可以直接获取方法参数,而拦截器只能获取到 HttpServletRequest 对象。

实战:定义切面获取请求参数

1. 基础切面定义

@Aspect
@Component
public class RequestParamAspect {

    // 定义切点:拦截所有 Controller 方法
    @Pointcut("execution(* com.example.controller..*.*(..))")
    public void controllerPointcut() {}

    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取方法参数名和值
        String[] paramNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        // 打印请求参数
        for (int i = 0; i < paramNames.length; i++) {log.info("参数名: {}, 参数值: {}", paramNames[i], args[i]);
        }

        // 执行原方法
        return joinPoint.proceed();}
}

2. 获取注解信息

// 获取方法上的注解
GetMapping getMapping = method.getAnnotation(GetMapping.class);
PostMapping postMapping = method.getAnnotation(PostMapping.class);

// 获取参数上的注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {for (Annotation annotation : parameterAnnotations[i]) {if (annotation instanceof RequestParam) {RequestParam requestParam = (RequestParam) annotation;
            String paramName = requestParam.value();
            // 处理 @RequestParam 注解的参数
        }
    }
}

3. 参数校验实现

// 在 around 方法中添加校验逻辑
for (int i = 0; i < args.length; i++) {if (args[i] == null) {throw new IllegalArgumentException(paramNames[i] + "不能为 null");
    }
    if (args[i] instanceof String && StringUtils.isEmpty((String) args[i])) {throw new IllegalArgumentException(paramNames[i] + "不能为空字符串");
    }
}

4. 异常统一处理

try {return joinPoint.proceed();
} catch (IllegalArgumentException e) {
    // 参数校验异常
    return Result.fail(400, e.getMessage());
} catch (Exception e) {
    // 其他异常
    return Result.fail(500, "系统异常");
}

性能优化建议

  1. 切面执行顺序 :通过 @Order 注解控制切面执行顺序,避免不必要的处理
  2. 减少切面数量 :合并功能相似的切面,减少代理链长度
  3. 条件匹配优化 :使用更精确的切点表达式,避免匹配不必要的方法
  4. 缓存反射结果 :Method 对象等可以缓存,避免重复反射

生产环境注意事项

  • 与 Spring Security 整合时,注意切面执行顺序要在授权之后
  • 微服务场景下,可以考虑将切面逻辑放到公共组件中
  • 对于高频接口,要特别关注切面的性能影响

扩展思考

  1. 参数自动解密 :可以在切面中识别特定注解,对加密参数自动解密
  2. 跨服务调用 :通过自定义注解标记需要传递的参数,在 Feign 拦截器中统一处理
  3. 参数脱敏 :在切面中实现对敏感数据的自动脱敏

总结

通过 Spring AOP 实现请求参数统一处理,可以使业务代码更加干净,同时提高系统的可维护性。关键在于:

  1. 合理设计切点表达式,精确匹配目标方法
  2. 处理好切面的执行顺序和异常处理
  3. 注意性能优化,避免过度使用 AOP

希望这篇实战指南能帮助你更好地使用 AOP 解决实际问题。如果有任何问题或建议,欢迎留言讨论。

正文完
 0
评论(没有评论)