You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:55:25