spring 默认用代理拦截 @async 调用。同一个类里直接调用自己的方法会绕过代理。把异步方法放到另一个 spring bean,再通过注入后的 bean 调用。

启用异步代理并命名执行器
在应用可扫描包中加入配置,示例给导出任务独立执行器。
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
class AsyncConfiguration {
@Bean("exportExecutor")
Executor exportExecutor() {
var executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(20);
executor.setThreadNamePrefix("export-");
return executor;
}
}
执行器由 Spring 管理生命周期,线程名前缀用于验证调用位置。
将异步方法放进单独 Bean
把下面类放在可扫描包内。示例只返回实际执行线程名。
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
class ExportWorker {
@Async("exportExecutor")
public CompletableFuture<string> run() {
return CompletableFuture.completedFuture(Thread.currentThread().getName());
}
}</string>

不要手动 new ExportWorker。调用方需要取得由 Spring 管理的 Bean。
从调用方核对执行线程
示例用 ApplicationRunner 验证。这里的 join 仅用于等结果并打印。
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class AsyncCheck {
@Bean
ApplicationRunner checkWorker(ExportWorker worker) {
return args -> {
System.out.println("caller: " + Thread.currentThread().getName());
System.out.println("worker: " + worker.run().join());
};
}
}
worker 行应带 export- 前缀。若线程名与调用线程相同,检查是否直接调用同类方法,或绕过了容器。











