You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SimpleAsyncTaskExecutor拦截器:启用请求作用域并传递请求Bean

Hey Brian, great question—let’s break this down step by step since you need to pass request-scoped beans to async tasks using SimpleAsyncTaskExecutor while keeping its "new thread per task" behavior (which is exactly its default behavior, so we don’t have to mess with that!).

Solution: Use a Task Decorator with SimpleAsyncTaskExecutor

The key here is to wrap the async task with a decorator that captures the current request context from the parent thread, then injects it into the new thread created by SimpleAsyncTaskExecutor. This lets Spring resolve your request-scoped bean correctly in the async context.

Step 1: Create a Request Context Task Decorator

First, make a decorator that saves the request attributes before the async task runs, then sets them in the new thread (and cleans up afterward to avoid memory leaks):

import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

public class RequestContextTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // Capture the current request context from the calling thread
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        
        return () -> {
            try {
                // Set the captured context in the async thread
                RequestContextHolder.setRequestAttributes(requestAttributes);
                // Run the original async task
                runnable.run();
            } finally {
                // Clean up the context to prevent memory leaks
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

Step 2: Configure a Custom SimpleAsyncTaskExecutor

Next, create a bean for your custom executor that uses the decorator. This keeps SimpleAsyncTaskExecutor's "new thread per task" behavior intact:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.SimpleAsyncTaskExecutor;

@Configuration
public class AsyncExecutorConfig {

    @Bean(name = "requestScopedAsyncExecutor")
    public TaskExecutor requestScopedAsyncExecutor() {
        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        // Attach our request context decorator
        executor.setTaskDecorator(new RequestContextTaskDecorator());
        // Optional: Set a thread name prefix for easier debugging
        executor.setThreadNamePrefix("request-scoped-async-");
        return executor;
    }
}

Step 3: Use the Executor in Your @Async Methods

Now just specify your custom executor in the @Async annotation, and your request-scoped bean will work as expected in the async thread:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class MyAsyncService {

    // Inject your request-scoped bean directly
    private final MyRequestScopedBean requestScopedBean;

    public MyAsyncService(MyRequestScopedBean requestScopedBean) {
        this.requestScopedBean = requestScopedBean;
    }

    @Async("requestScopedAsyncExecutor")
    public void runAsyncTask() {
        // You can now use the request-scoped bean normally here
        String requestSpecificValue = requestScopedBean.getSomeValue();
        // ... your async business logic ...
    }
}

Bonus: Global Async Configuration

If you want all @Async methods to use this executor by default, implement AsyncConfigurer instead:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.SimpleAsyncTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class GlobalAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        executor.setTaskDecorator(new RequestContextTaskDecorator());
        executor.setThreadNamePrefix("global-request-scoped-async-");
        return executor;
    }
}

Why This Works

  • SimpleAsyncTaskExecutor creates a new thread for every task by default—perfect for your requirement of no thread reuse.
  • The decorator copies the request context from the original request thread to the new async thread, so Spring can resolve your request-scoped bean just like it would in the main request thread.
  • The finally block ensures we clean up the context after the task finishes, preventing memory leaks.

内容的提问来源于stack exchange,提问作者Brian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:47:48