请解释 Spring 中 @Async 注解的内部实现机制和工作原理。
考察说明
考查对 Spring 异步执行机制和动态代理原理的理解。
回答思路
- 【回答框架 1】@Async 依赖 Spring AOP 的动态代理机制。当 Bean 被创建时,若检测到 @Async 注解,Spring 会为 Bean 生成代理对象,并在代理中拦截带有 @Async 的方法调用。
- 【回答框架 2】在代理拦截方法时,会从容器中获取 TaskExecutor(默认为 SimpleAsyncTaskExecutor),将方法调用包装成任务并提交给执行器异步执行。方法调用本身会立即返回,执行流程转入异步线程。
- 【回答框架 3】底层通过 AOP 的拦截器链实现,具体是 AsyncExecutionInterceptor。关键要点包括:直接调用 this.method() 不会触发代理,因此同类内部调用 @Async 方法会失效;且异步方法执行是在独立线程中,所以无法获取原线程的 ThreadLocal 上下文。
- 【回答框架 4】自 Spring 3.0 引入,需要在配置类上使用 @EnableAsync 开启。默认执行器是 SimpleAsyncTaskExecutor,它每次新建线程,不推荐生产环境使用,通常替换为 ThreadPoolTaskExecutor 并配置核心线程数和队列。
- 【关键点 1】@Async 基于 Spring AOP 动态代理实现。
- 【关键点 2】方法调用被 AsyncExecutionInterceptor 拦截,提交给 TaskExecutor 异步执行。
- 【关键点 3】同类内部调用或自调用会导致代理失效,方法同步执行。
- 【关键点 4】异步方法运行在独立线程,原线程的 ThreadLocal 和事务上下文无法传递。
- 【关键点 5】必须 @EnableAsync 开启,否则注解不生效。
- 【易错点 1】认为 @Async 不依赖代理或无需开启,导致异步未生效。
- 【易错点 2】忽略内部调用或自调用时代理失效的问题。
- 【易错点 3】默认 SimpleAsyncTaskExecutor 不重用线程,生产环境需配置线程池。