多线程场景中的Spring Request

时间:2019-03-18 16:35:51

标签: spring multithreading spring-boot configuration resttemplate

如何从另一个线程(不是接收请求的线程)访问请求属性? 我有一个多线程方案,其中有一个ClientHttpRequestInterceptor,它正在执行某种魔术来处理传出的请求标头(从传入的请求标头转发值),并且该过程需要并行完成。 我当前的问题是当我声明将传入的标头存储为Bean的对象时(scope = RequestScope)Spring给了我这样的错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.headersHolder': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

这是我提供给Spring的配置:

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
  private static final String REQUEST_SCOPE = "request";

  @Bean
  @RequestScope
  public HeadersHolder headersHolder() {
    logger.info("Creating new HeadersHolder instance.");
    return new HeadersHolder();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new HeadersInterceptor() {
      @Override
      public HeadersHolder getHeadersHolder() {
        return headersHolder();
      }
    });
  }

  @Bean
  @RequestScope
  public ClientHttpRequestInterceptor headersClientInterceptor() {
    return new RequestInterceptor() {
      @Override
      protected HeadersHolder getHeadersHolder() {
        return headersHolder();
      }
    };
  }

  private RestTemplateBuilder builder() {
    logger.info("Providing custom Rest Template Builder with custom Rest Template customizer");
    RestTemplateBuilder builder = new RestTemplateBuilder();
    return builder.messageConverters(new RestTemplate().getMessageConverters());
  }

  @Profile("!test")
  @Bean
  @LoadBalanced
  public RestTemplate loadBalancedRestTemplate() {
    logger.info("Providing custom load balanced Rest Template");
    return builder()
        .setConnectTimeout(20000)
        .setReadTimeout(20000)
        .interceptors(headersClientInterceptor())
        .build();
  }

  @Profile("test")
  @Bean
  @DependsOn(value = {"restTemplateBuilder"})
  public RestTemplate normalRestTemplate() {
    logger.info("Providing custom NOT load balanced Rest Template");
    return builder()
        .interceptors(headersClientInterceptor())
        .build();
  }
}

这是标题拦截器:

public abstract class HeadersInterceptor extends HandlerInterceptorAdapter {

  public abstract HeadersHolder getHeadersHolder();

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    getHeadersHolder().readFrom(request);
    return true;
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    getHeadersHolder().writeTo(response);
    getHeadersHolder().clear();
  }
}

这是ClientHttpRequestInterceptor:

public abstract class RequestInterceptor implements ClientHttpRequestInterceptor {
  protected abstract HeadersHolder getHeadersHolder();

  @Override
  public ClientHttpResponse intercept(HttpRequest clientRequest, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    HeadersHolder headersHolder = getHeadersHolder();

    /* ... here I do modify the headers for the outcoming request ...
     * ... based on some values that are in the incoming request.
    */

    return execution.execute(clientRequest, body);
  }
}

我将这个RestTemplate实例注入到@Service(单个实例)中,该服务并行调用REST API客户端包装(使用RestTemplate实例的包装)。

0 个答案:

没有答案