@Async异步日志及@EnableAsync底层机制

面试速记

@EnableAsync 通过 @Import 引入 ProxyAsyncConfiguration激活异步处理,获取到核心是AsyncAnnotationBeanPostProcessor,会在 Bean 初始化阶段扫描 @Async 方法,利用 AOP 动态代理生成代理对象。当调用被 @Async 标记的方法时,代理会通过 AsyncExecutionInterceptor 拦截请求,解析线程池(优先自定义,否则默认),将任务封装为 Callable 提交到线程池异步执行。

简单实例

针对需要获取到执行结果做记录的日志操作,除了环绕通知中方法执行后获取到返回值处理,还可以在controller方法内调用目标方法返回之前进行异步日志记录,步骤如下:

  /**
   * 删除角色
   */
@Override
@PostMapping("/remove")
public ResponseObject<Boolean> groupDel(@RequestBody RoleUpdateReqDTO reqDTO) {
     ResponseObject<Boolean> result = roleService.groupDel(reqDTO);
     // 异步新增操作日志
     roleService.deleteRoleOperateLogAdd(result, reqDTO.getRoleId());
     return result;
}

service层中对参数进行处理封装后最终调用这个方法:

@Async("operateLogExecutor")//指定线程池异步执行
    public void asyncOperateLogAdd(OperateLogDO operateLog) {
        operateLogMapper.insert(operateLog);
    }

通过参数指定线程池操作实现异步记录日志,此处需要配置线程池,并且开启@EnableAsync

@EnableAsync//开启异步执行机制
@Configuration
public class ThreadPoolConf {
    /**
     * 线程池配置
     */
    @Bean
    public Executor operateLogExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数量
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 设置最大线程池大小
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
        // 设置队列容量为200个
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("commander-manager-operateLogExecutor-");
        // 由调用线程处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 线城池等待其它任务完成再销毁
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线城池的等待时间,如果超过这个时间还没有销毁就强制销毁.
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }

@EnableAsync底层机制

@EnableAsync 是 Spring 异步编程的核心注解,其底层机制涉及 代理模式(AOP)后置处理器(BeanPostProcessor)任务执行器(TaskExecutor) 的协同工作。以下是其激活 @Async 处理的底层实现原理:

1. 启用异步的入口:@EnableAsync

当在配置类上添加 @EnableAsync 时,Spring 会通过 @Import 机制 加载异步处理的核心组件 AsyncConfigurationSelector,其作用如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)//加载异步处理的核心组件
public @interface EnableAsync {
    // ...
}
  • AsyncConfigurationSelector:根据 Spring 版本或配置,选择具体的异步配置类。

    • 默认情况下,选择 ProxyAsyncConfiguration(基于代理的异步配置)。
    • 若需要响应式编程支持,可能选择其他配置类(如 ReactiveAsyncConfiguration)。
    public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    
    	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
    			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    
    	@Override
    	@Nullable
    	public String[] selectImports(AdviceMode adviceMode) {
    		switch (adviceMode) {
    			case PROXY:
    				return new String[] {ProxyAsyncConfiguration.class.getName()};
    			case ASPECTJ:
    				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
    			default:
    				return null;
    		}
    	}
    
    }
    

2. 核心配置类:ProxyAsyncConfiguration

ProxyAsyncConfiguration 是异步处理的 配置中枢,主要负责注册 AsyncAnnotationBeanPostProcessor(关键后置处理器):

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        // 创建 AsyncAnnotationBeanPostProcessor
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        // 配置自定义的 AsyncAnnotationAdvisor(负责处理 @Async 注解)
        bpp.configure(this.executor, this.exceptionHandler);
        // 设置代理目标类(CGLIB 或 JDK 动态代理)
        bpp.setProxyTargetClass(this.proxyTargetClass);
        bpp.setOrder(this.order);
        return bpp;
    }
}

3. 异步注解处理器:AsyncAnnotationBeanPostProcessor

AsyncAnnotationBeanPostProcessorBean 后置处理器,负责在 Bean 初始化阶段扫描 @Async 注解,并为其创建 AOP 代理:

关键步骤

  1. Bean 初始化后处理postProcessAfterInitialization 方法中,遍历所有 Spring Bean,检查是否存在 @Async 注解的方法。
  2. 创建 Advisor 通过 AsyncAnnotationAdvisor 创建 AOP 切面,定义以下内容:
    • Pointcut:匹配所有被 @Async 注解标记的方法。
    • Advice:使用 AsyncExecutionInterceptor 拦截方法调用,将其提交到线程池异步执行。
  3. 生成代理对象 若 Bean 中存在 @Async 方法,则通过 JDK 动态代理CGLIB 代理 生成代理对象,替换原始 Bean。

4. 方法拦截器:AsyncExecutionInterceptor

AsyncExecutionInterceptor 是实际处理异步执行的核心拦截器,继承自 MethodInterceptor。其工作流程如下:

拦截方法调用

  1. 确定线程池 根据 @Async 注解的 value 参数(如 operateLogExecutor),查找对应的 TaskExecutor 线程池:
    • 若未指定线程池名称,使用默认的 TaskExecutor(默认是 SimpleAsyncTaskExecutor)。
    • 若找不到指定线程池,抛出异常。
  2. 提交任务到线程池 将原始方法的调用封装为 Callable 任务,提交到线程池:
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // 获取线程池
    Executor executor = getExecutor(invocation.getMethod());
    // 封装为 Callable 任务
    Callable<Object> task = () -> {
        try {
            return invocation.proceed();
        } catch (Throwable ex) {
            throw new IllegalStateException("Async method异常", ex);
        }
    };
    // 提交到线程池
    return doSubmit(task, executor, invocation.getMethod().getReturnType());
}

处理返回值

  • 若方法返回 FutureCompletableFuture,返回实际的异步结果。
  • 若方法无返回值(void),直接提交任务,不等待结果。

5. 线程池选择与执行

线程池解析逻辑

  • @Async 指定名称:优先从 Spring 容器中查找对应名称的 Executor Bean。
  • 未指定名称:查找默认的 Executor Bean(默认名称为 taskExecutor 或通过 AsyncConfigurer 配置)。
  • 无任何配置:回退到 SimpleAsyncTaskExecutor(每次创建新线程,不推荐生产使用)。

线程池任务执行

  • 核心线程池参数:通过 ThreadPoolTaskExecutor 配置核心线程数、队列容量、拒绝策略等。
  • 任务提交顺序:优先使用核心线程 → 队列 → 最大线程 → 拒绝策略。

6. 上下文传递

默认情况下,异步线程不会继承主线程的上下文(如 SecurityContextTransaction)。若需传递上下文,可通过以下方式:

  • TaskDecorator:自定义装饰器,复制上下文到子线程:
@Bean("operateLogExecutor")
public Executor operateLogExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setTaskDecorator(new ContextCopyingTaskDecorator());
    // 其他配置...
    return executor;
}

总结:@EnableAsync 的底层流程

  1. 启动阶段 @EnableAsync 导入 ProxyAsyncConfiguration,注册 AsyncAnnotationBeanPostProcessor
  2. Bean 初始化阶段 AsyncAnnotationBeanPostProcessor 扫描所有 Bean,为包含 @Async 方法的 Bean 创建代理。
  3. 方法调用阶段 代理对象拦截 @Async 方法调用,通过 AsyncExecutionInterceptor 将任务提交到线程池。
  4. 任务执行阶段 线程池分配独立线程执行原始方法逻辑,主线程继续执行后续流程。

关键源码类

类名 作用
AsyncAnnotationBeanPostProcessor 扫描 @Async 方法并创建代理。
AsyncAnnotationAdvisor 定义切面逻辑(Pointcut + Advice)。
AsyncExecutionInterceptor 拦截方法调用,提交任务到线程池。
ThreadPoolTaskExecutor Spring 封装的线程池实现,替代原生 ThreadPoolExecutor
SimpleAsyncTaskExecutor 默认线程池(非池化,每次新建线程)。
#java#
全部评论

相关推荐

哇哇的菜鸡oc:他这不叫校招offer,而是实习offer
点赞 评论 收藏
分享
09-17 19:25
已编辑
太原理工大学 游戏测试
叁六玖:公司名发我,我要这个HR带我打瓦
我的秋招日记
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务