Spring/Spring Boot中如何通过AOP更新请求头参数并传递给控制器(仅改首参)
解决Spring Boot AOP修改请求参数/头并传递给控制器的问题
一、为什么@Before设置request.setAttribute控制器拿不到值?
你之前用request.setAttribute("traceId", ...)后控制器获取到null,核心原因是控制器方法的参数默认从请求参数(@RequestParam)、路径变量(@PathVariable)或者请求头(@RequestHeader)中取值,而request.setAttribute()是把值存在请求的属性域里,要获取这个值需要在控制器参数上标注@RequestAttribute("traceId"),比如:
@GetMapping("/test") public String test(@RequestAttribute("traceId") String traceId) { // 这里就能拿到设置的traceId了 }
但如果你的需求是让控制器直接通过普通参数(比如String traceId)拿到值,这种方式就不适用,需要换一种思路修改方法参数或者请求头。
二、用@Around仅修改第一个参数(traceId),其余参数保持不变
你之前的@Around写法需要手动传入所有参数,确实不够灵活。我们可以直接获取方法的参数数组,修改第一个元素后再执行方法,这样不管控制器方法有多少参数,都能只更新traceId:
@Around("execution(* com.test.api.*.*(..))") public Object setTraceId(ProceedingJoinPoint joinPoint) throws Throwable { // 获取原方法的参数数组 Object[] args = joinPoint.getArgs(); // 检查参数数组不为空,且第一个参数是String类型(符合你说的所有方法第一个参数都是traceId的情况) if (args != null && args.length > 0 && args[0] instanceof String) { String newTraceId = ServiceUtil.getTraceId(); // 修改第一个参数为新的traceId args[0] = newTraceId; } // 用修改后的参数执行原方法 return joinPoint.proceed(args); }
这个写法的优势很明显:
- 不需要硬编码参数数量,自动适配所有控制器方法
- 只修改第一个参数,其余参数完全保留原有值
- 不需要在切点表达式里绑定
args(traceId,..),代码更简洁灵活
三、如果需求是修改请求头(而非方法参数)
如果你是想让控制器通过@RequestHeader("traceId")拿到值,那原生的HttpServletRequest的header是不可修改的,需要用HttpServletRequestWrapper包装请求,替换请求上下文里的request:
@Around("PointcutDefinition.controllerLayer()") public Object modifyRequestHeader(ProceedingJoinPoint joinPoint) throws Throwable { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest originalRequest = attributes.getRequest(); // 包装请求,重写header获取方法 HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(originalRequest) { @Override public String getHeader(String name) { // 当获取traceId头时,返回新生成的ID if ("traceId".equals(name)) { return ServiceUtil.getTraceId(); } return super.getHeader(name); } @Override public Enumeration<String> getHeaders(String name) { if ("traceId".equals(name)) { return Collections.enumeration(Collections.singletonList(ServiceUtil.getTraceId())); } return super.getHeaders(name); } }; // 替换请求上下文里的request attributes.setRequest(wrappedRequest); } return joinPoint.proceed(); }
这样控制器里用@RequestHeader("traceId") String traceId就能拿到更新后的traceId了。
内容的提问来源于stack exchange,提问作者Thiagarajan Ramanathan




