[SPRING] 비동기 태스크 실행 프로그램에서 요청 범위를 사용하는 방법
SPRING비동기 태스크 실행 프로그램에서 요청 범위를 사용하는 방법
내 응용 프로그램에서 일부 비동기 웹 서비스가 있습니다. 서버가 요청을 수락하고 OK 응답을 반환하고 AsyncTaskExecutor를 사용하여 처리 요청을 시작합니다. 내 질문은 요청 범위를 여기에서 사용 가능하게하는 방법이다. 왜냐하면이 처리에서 나는 다음과 같이 주석이 달린 클래스를 가져와야하기 때문이다 :
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
이제 예외가 생깁니다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestContextImpl': 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.
DispatcherServlet이 아니라 SimpleAsyncTaskExecutor에서 실행되기 때문에
내 비동기 요청 처리
taskExecutor.execute(new Runnable() {
@Override
public void run() {
asyncRequest(request);
}
});
여기서 taskExecutor는 다음과 같습니다.
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
해결법
-
==============================
1.@Async를 사용하여 백그라운드에서 코드를 실행하는 데 필요한 동일한 문제가 발생하여 Session 또는 RequestScope 빈을 사용할 수 없었습니다. 우리는 다음과 같은 방법으로 그것을 해결했습니다 :
@Async를 사용하여 백그라운드에서 코드를 실행하는 데 필요한 동일한 문제가 발생하여 Session 또는 RequestScope 빈을 사용할 수 없었습니다. 우리는 다음과 같은 방법으로 그것을 해결했습니다 :
참고 : 이것은 세션 및 요청 범위 콩에서만 작동하며 보안 컨텍스트에서는 작동하지 않습니다 (Spring Security에서와 같이). 보안 컨텍스트를 설정하는 데 다른 방법을 사용해야합니다.
주 2 : 간결성을 위해 Callable 및 submit () 구현 만 표시했습니다. Runnable과 execute ()에 대해서도 동일한 작업을 수행 할 수 있습니다.
다음은 코드입니다.
집행자:
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor { @Override public <T> Future<T> submit(Callable<T> task) { return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes())); } @Override public <T> ListenableFuture<T> submitListenable(Callable<T> task) { return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes())); } }
호출 가능 :
public class ContextAwareCallable<T> implements Callable<T> { private Callable<T> task; private RequestAttributes context; public ContextAwareCallable(Callable<T> task, RequestAttributes context) { this.task = task; this.context = context; } @Override public T call() throws Exception { if (context != null) { RequestContextHolder.setRequestAttributes(context); } try { return task.call(); } finally { RequestContextHolder.resetRequestAttributes(); } } }
구성 :
@Configuration public class ExecutorConfig extends AsyncConfigurerSupport { @Override @Bean public Executor getAsyncExecutor() { return new ContextAwarePoolExecutor(); } }
-
==============================
2.원래의 부모 요청 처리 스레드가 클라이언트에 대한 응답을 이미 커밋하고 모든 요청 객체가 파괴되었으므로 하위 비동기 스레드에서 요청 범위 개체를 가져올 수 없습니다. 이러한 시나리오를 처리하는 한 가지 방법은 SimpleThreadScope와 같은 사용자 지정 범위를 사용하는 것입니다.
원래의 부모 요청 처리 스레드가 클라이언트에 대한 응답을 이미 커밋하고 모든 요청 객체가 파괴되었으므로 하위 비동기 스레드에서 요청 범위 개체를 가져올 수 없습니다. 이러한 시나리오를 처리하는 한 가지 방법은 SimpleThreadScope와 같은 사용자 지정 범위를 사용하는 것입니다.
SimpleThreadScope의 문제점 중 하나는 내부 ThreadLocal을 사용하기 때문에 자식 스레드가 부모 범위 변수를 상속받지 않는다는 것입니다. 이를 극복하기 위해 SimpleThreadScope와 완전히 유사하지만 InheritableThreadLocal을 내부적으로 사용하는 사용자 정의 범위를 구현하십시오. 더 많은 정보를 원하시면 Spring MVC : 스폰 된 스레드 내에서 요청 범위의 bean을 사용하는 방법은 무엇입니까?
-
==============================
3.가장 쉬운 방법은 다음과 같은 작업 꾸미기를 사용하는 것입니다.
가장 쉬운 방법은 다음과 같은 작업 꾸미기를 사용하는 것입니다.
static class ContextCopyingDecorator implements TaskDecorator { @Nonnull @Override public Runnable decorate(@Nonnull Runnable runnable) { RequestAttributes context = RequestContextHolder.currentRequestAttributes(); Map<String, String> contextMap = MDC.getCopyOfContextMap(); return () -> { try { RequestContextHolder.setRequestAttributes(context); MDC.setContextMap(contextMap); runnable.run(); } finally { MDC.clear(); RequestContextHolder.resetRequestAttributes(); } }; } }
이 데코레이터를 태스크 실행자에 추가하려면 구성 루틴에 추가해야합니다.
@Override @Bean public Executor getAsyncExecutor() { ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor(); poolExecutor.setTaskDecorator(new ContextCopyingDecorator()); poolExecutor.initialize(); return poolExecutor; }
추가 소유자 또는 사용자 정의 스레드 풀 태스크 실행 프로그램이 필요하지 않습니다.
from https://stackoverflow.com/questions/23732089/how-to-enable-request-scope-in-async-task-executor by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring Boot + Spring Security 애플리케이션에서 CORS를 구성하는 방법은 무엇입니까? (0) | 2018.12.05 |
---|---|
[SPRING] 톰캣에 전개 된 스프링 부트 전쟁 (0) | 2018.12.05 |
[SPRING] 스프링 데이터 소스를 동적으로 변경 (0) | 2018.12.05 |
[SPRING] Spring Web App Context에 프로그램 적으로 Bean 추가하기 (0) | 2018.12.05 |
[SPRING] Spring - POST 후에 리디렉션 (유효성 검사 오류가있는 경우에도) (0) | 2018.12.05 |