Spring Boot异步:在异步方法中获取HttpServletRequest

3

我已经在我的Spring Boot应用程序中启用了异步功能:

@Configuration
@EnableAsync
public class BackOfficeConfiguration {}

我已经创建了这个异步方法:

@Async
public void importDocuments() {}

importDocuments 代码只是:

@Async
public void importDocuments() {
    // Do something

    // Get current request context
    ServletRequestAttributes requestAttributes = 
        (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    HttpServletRequest request = requestAttributes.getRequest();
}

当我使用RequestContextHolder.currentRequestAttributes()时,在spring boot中出现了以下异常:

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.

如何在异步方法中获取当前的HttpServletRequest


2
不要这样做。将所需的对象/数据传递给方法,或者在您的Web层中使用适当的异步处理。 - undefined
2个回答

3

如果您在方法上使用了@Async,那么这意味着该方法会抛到另一个新线程(独立的线程)中。这意味着它与当前请求无关(这是两条路)。如果您想从请求中获取一些数据,必须从请求中获取所需数据并将其保存为副本。因为在完成请求后,数据将被删除。如果您已经传递了引用,那么在访问数据时会出现错误,如No thread-bound request found。您必须创建一个可克隆的对象,并将数据填充到该对象中,并在调用异步方法之前获取一个克隆,并通过传递该对象来调用异步方法。然后,您就有了该数据的副本,可以在新线程(@Async方法)中使用它。下面是一个可克隆类的示例。

public class CloneableClass implements Cloneable {
    //
    private ServletRequestAttributes requestAttributes;
    //getters
    //setters

    @Override
    public CloneableClass  clone() {
        try {
            CloneableClass clone = (CloneableClass) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

}

请在https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html上阅读更多关于此主题的信息。


0
这是我基于https://dev59.com/62Ag5IYBdhLWcg3wU5gS#33337838的工作解决方案。

AsyncContextAwarePoolExecutor.java

public class AsyncContextAwarePoolExecutor extends ThreadPoolTaskExecutor {

    public static final String BEAN_NAME = "AsyncPoolExecutor";

    private static class ContextAwareCallable<T> implements Callable<T> {
        private Callable<T>       task;
        private RequestAttributes context;

        public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
            this.context = context;
            this.task    = task;
        }

        @Override
        public T call() throws Exception {
            if (null != context) {
                RequestContextHolder.setRequestAttributes(context);
            }

            try {
                return task.call();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }
    }

    private <T> ContextAwareCallable getContextAwareCallable(Callable<T> task) {
        return new ContextAwareCallable(
            task,
            RequestContextHolder.currentRequestAttributes()
        );
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(
            getContextAwareCallable(task)
        );
    }

    @Override
    public <T> CompletableFuture<T> submitCompletable(Callable<T> task) {
        return super.submitCompletable(
            getContextAwareCallable(task)
        );
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        return super.submitListenable(
            getContextAwareCallable(task)
        );
    }

}

AsyncExceptionHandler.java

@Slf4j
public final class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
        log.error(
            throwable.getMessage(),
            throwable
        );
    }

}

AsyncConfiguration.java

@Configuration
@EnableAsync
public abstract class AsyncConfiguration implements AsyncConfigurer {

    @Autowired
    private AsyncSettings asyncSettings;

    @Override
    public Executor getAsyncExecutor() {
        AsyncSettings.Thread.Prefix   prefix   = asyncSettings.getThread().getPrefix();
        AsyncSettings.Pool.Size       size     = asyncSettings.getPool().getSize();

        AsyncContextAwarePoolExecutor executor = new AsyncContextAwarePoolExecutor();

        executor.setBeanName(AsyncContextAwarePoolExecutor.BEAN_NAME);
        executor.setCorePoolSize(size.getCore());
        executor.setMaxPoolSize(size.getMax());
        executor.setQueueCapacity(asyncSettings.getQueue().getCapacity());
        executor.setThreadGroupName(prefix.getGroup());
        executor.setThreadNamePrefix(prefix.getThread());

        executor.initialize();

        return executor;
    }

    @Bean(AsyncContextAwarePoolExecutor.BEAN_NAME)
    public Executor getAsyncExecutorBean() {
        return getAsyncExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }

}

SpringBoot 控制器

@Autowired
private DefaultApiLogic defaultApiLogic;
    
@Async(AsyncContextAwarePoolExecutor.BEAN_NAME)
public CompletableFuture<ResponseEntity<Version>> getVersion() {

    String            requestBaseUrl;
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

    if (requestAttributes != null) {
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        requestBaseUrl             = request.getRequestURL().toString();
    } else {
        // Lambda effective initializer
        requestBaseUrl = null;
    }

    return CompletableFuture.supplyAsync(
        () -> ResponseEntity.ok(
            defaultApiLogic.getVersion(requestBaseUrl)
        ),
        Runnable::run
    );
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接