Spring切面生成的CheckResponse如何传入RestController方法(不覆盖参数)?
可行的优雅解决方案
针对你的需求,这里提供三种无需修改控制器原有参数、通用且优雅的实现方案:
方案1:请求作用域Holder类
通过定义一个请求作用域的Bean来存储CheckResponse,切面中设置值,控制器注入该Bean即可获取。
步骤1:定义请求作用域Holder
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; @Component @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public class CheckResponseHolder { private CheckResponse checkResponse; public CheckResponse getCheckResponse() { return checkResponse; } public void setCheckResponse(CheckResponse checkResponse) { this.checkResponse = checkResponse; } }
proxyMode = TARGET_CLASS确保注入的是代理对象,即使在非请求环境下也能正常引用。
步骤2:修改切面设置Holder值
// 注入CheckResponseHolder private final CheckResponseHolder checkResponseHolder; @Around("execution(* (@org.springframework.web.bind.annotation.RestController *).*(..))") public Object check(final ProceedingJoinPoint joinPoint) throws Throwable { CheckResponse checkResponse = client.check(); if (checkResponse.getDate().before(new Date())) { throw new CheckException("message"); } // 将检查结果存入Holder checkResponseHolder.setCheckResponse(checkResponse); try { return joinPoint.proceed(); } finally { // 请求结束后清理,避免残留 checkResponseHolder.setCheckResponse(null); } }
步骤3:控制器中获取
@RestController public class DemoController { private final CheckResponseHolder checkResponseHolder; // 构造注入Holder public DemoController(CheckResponseHolder checkResponseHolder) { this.checkResponseHolder = checkResponseHolder; } @GetMapping("/demo") public String demo(@RequestParam String param) { CheckResponse response = checkResponseHolder.getCheckResponse(); // 使用checkResponse做业务逻辑 return "success"; } }
方案2:Spring MVC请求属性+@RequestAttribute
利用Spring MVC内置的请求属性存储CheckResponse,控制器通过@RequestAttribute直接注入,无需额外定义Bean。
步骤1:修改切面存入请求属性
@Around("execution(* (@org.springframework.web.bind.annotation.RestController *).*(..))") public Object check(final ProceedingJoinPoint joinPoint) throws Throwable { CheckResponse checkResponse = client.check(); if (checkResponse.getDate().before(new Date())) { throw new CheckException("message"); } // 获取当前请求属性,存入检查结果 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes != null) { requestAttributes.setAttribute("CURRENT_CHECK_RESPONSE", checkResponse, RequestAttributes.SCOPE_REQUEST); } try { return joinPoint.proceed(); } finally { // 清理属性 if (requestAttributes != null) { requestAttributes.removeAttribute("CURRENT_CHECK_RESPONSE", RequestAttributes.SCOPE_REQUEST); } } }
步骤2:控制器中通过注解获取
@RestController public class DemoController { @GetMapping("/demo") public String demo(@RequestParam String param, @RequestAttribute("CURRENT_CHECK_RESPONSE") CheckResponse response) { // 直接使用注入的checkResponse return "success"; } }
也可以手动从RequestContextHolder获取,适合不想修改方法参数的场景:
CheckResponse response = (CheckResponse) RequestContextHolder.getRequestAttributes() .getAttribute("CURRENT_CHECK_RESPONSE", RequestAttributes.SCOPE_REQUEST);
方案3:自定义注解+方法参数解析器(推荐)
通过自定义参数注解和Spring MVC参数解析器,让控制器方法可以像使用@RequestParam一样直接注入CheckResponse,风格统一且扩展性强。
步骤1:自定义参数注解
import java.lang.annotation.*; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface CurrentCheckResponse { }
步骤2:实现参数解析器
import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; public class CheckResponseArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { // 判断参数是否带有自定义注解且类型为CheckResponse return parameter.hasParameterAnnotation(CurrentCheckResponse.class) && CheckResponse.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { // 从请求属性中获取检查结果(和方案2的切面逻辑配合) return webRequest.getAttribute("CURRENT_CHECK_RESPONSE", NativeWebRequest.SCOPE_REQUEST); } }
步骤3:注册参数解析器
在starter的配置类中注册解析器,让Spring MVC识别:
import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class CheckAutoConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new CheckResponseArgumentResolver()); } }
步骤4:控制器中使用自定义注解
@RestController public class DemoController { @GetMapping("/demo") public String demo(@RequestParam String param, @CurrentCheckResponse CheckResponse response) { // 直接使用注入的checkResponse,和原生参数注解风格一致 return "success"; } }
方案对比
- 方案1:适合需要在多个组件(如Service、Interceptor)中共享
CheckResponse的场景,代码侵入性低,但需要注入Holder类。 - 方案2:无需额外定义Bean,实现简单,适合仅在控制器中使用的场景。
- 方案3:最符合Spring MVC的参数注入风格,代码可读性高,扩展性强,推荐作为优先方案。
内容的提问来源于stack exchange,提问作者Slava Ugnachyov
相关产品推荐
相关产品推荐

