共计 2267 个字符,预计需要花费 6 分钟才能阅读完成。
为什么需要统一处理请求参数?
在日常 Web 开发中,我们经常遇到这些场景:

- 需要对所有接口的入参进行非空校验或格式校验
- 要记录关键接口的请求参数日志
- 某些敏感接口需要做参数解密
- 需要统计接口调用时传递的特定参数
如果每个接口都手动实现这些逻辑,会导致大量重复代码,维护困难。而 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, "系统异常");
}
性能优化建议
- 切面执行顺序 :通过 @Order 注解控制切面执行顺序,避免不必要的处理
- 减少切面数量 :合并功能相似的切面,减少代理链长度
- 条件匹配优化 :使用更精确的切点表达式,避免匹配不必要的方法
- 缓存反射结果 :Method 对象等可以缓存,避免重复反射
生产环境注意事项
- 与 Spring Security 整合时,注意切面执行顺序要在授权之后
- 微服务场景下,可以考虑将切面逻辑放到公共组件中
- 对于高频接口,要特别关注切面的性能影响
扩展思考
- 参数自动解密 :可以在切面中识别特定注解,对加密参数自动解密
- 跨服务调用 :通过自定义注解标记需要传递的参数,在 Feign 拦截器中统一处理
- 参数脱敏 :在切面中实现对敏感数据的自动脱敏
总结
通过 Spring AOP 实现请求参数统一处理,可以使业务代码更加干净,同时提高系统的可维护性。关键在于:
- 合理设计切点表达式,精确匹配目标方法
- 处理好切面的执行顺序和异常处理
- 注意性能优化,避免过度使用 AOP
希望这篇实战指南能帮助你更好地使用 AOP 解决实际问题。如果有任何问题或建议,欢迎留言讨论。
正文完
