Spring项目使用@Async导致request参数获取为空的问题

479 阅读1分钟

最近工作中有需求做excel异步导出功能,发现在使用@Async后,子线程中获取不到request中的参数信息,故对此问题进行记录

@Ascyn基本配置(旧)

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig {
    @Value("${async.executor.thread.core_pool_size:10}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size:20}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity:2000}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix:async-job}")
    private String namePrefix;

    @Bean(name = "jobExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }

}

@Ascyn基本配置(新)

原来的配置需要进行优化,添加: executor.setTaskDecorator(new SubThreadTaskDecorator());

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig {
    @Value("${async.executor.thread.core_pool_size:10}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size:20}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity:2000}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix:async-job}")
    private String namePrefix;

    @Bean(name = "jobExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // ===== 自己实现TaskDecorator =====
        executor.setTaskDecorator(new SubThreadTaskDecorator());
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

SubThreadTaskDecorator.java

主要的逻辑在于request.startAsync()

public class SubThreadTaskDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable runnable) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        AsyncContext asyncContext = null;
        if (attributes!=null) {
            HttpServletRequest request = attributes.getRequest();
            // 开启异步
            asyncContext = request.startAsync();
            // 设置超时时间
            asyncContext.setTimeout(999999);
        }
        AsyncContext finalAsyncContext = asyncContext;
        return () -> {
            try {
                RequestContextHolder.setRequestAttributes(attributes,true);
                runnable.run();
            } finally {
                RequestContextHolder.resetRequestAttributes();
                if (finalAsyncContext !=null) {
                    finalAsyncContext.complete();
                }
            }
        };
    }
}

注意:servlet默认的异步处理时间为10秒,如果你的子线程执行时间会超过10秒的话,记得设置asyncContext.setTimeout(999999),否则会报: The request associated with the AsyncContext has already completed processing.