Spring Request в многопоточном сценарии - PullRequest
0 голосов
/ 18 марта 2019

Как я могу получить доступ к атрибутам запроса из другого потока, который не является тем, который получает запрос?У меня есть сценарий многопоточности, где у меня есть 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 (экземпляр singleton), который параллельно вызывает оболочку клиента REST API (использующую экземпляр RestTemplate).

...