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

Spring Boot @Async嵌套调用不生效问题排查

Spring @Async服务内部嵌套调用不生效的解决方案

问题场景

已通过@EnableAsync开启Spring异步支持,自定义了前缀为ASYNC-的线程池,@Async注解在@RestController中能正常触发异步执行,但在服务内部嵌套调用BatchLogGateway#writeBatchLog()方法时,INSERT操作始终在原线程执行,日志中看不到自定义的异步线程编号。

核心原因

Spring的@Async依赖动态代理实现,只有通过Spring容器生成的代理对象调用方法时,异步增强逻辑才会生效。如果在服务内部直接调用自身的@Async方法(绕过代理),会直接执行原始方法,无法触发异步。

解决方案

1. 通过Spring容器获取代理对象调用

在服务类中注入ApplicationContext,通过容器获取自身的代理实例,再用代理对象调用异步方法:

@Service
public class BatchLogGateway {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private ApplicationContext applicationContext;
    private static final Logger logger = LoggerFactory.getLogger(BatchLogGateway.class);

    @Async
    public void writeBatchLog(int batchNo, String errorTxt) {
        MapSqlParameterSource namedParameters = new MapSqlParameterSource();
        namedParameters.addValue("batchNo", batchNo);
        namedParameters.addValue("error", errorTxt);
        try {
            jdbcTemplate.update("INSERT INTO batches_error_log (error_log, batch_no) values (:error, :batchNo)", namedParameters);
        } catch (Exception e) {
            logger.error("writing into batchLog {} was unsuccessful", batchNo);
        }
    }

    // 内部调用时使用此方法
    public void triggerWriteBatchLog(int batchNo, String errorTxt) {
        BatchLogGateway proxy = applicationContext.getBean(BatchLogGateway.class);
        proxy.writeBatchLog(batchNo, errorTxt);
    }
}

内部调用时,调用triggerWriteBatchLog而非直接调用writeBatchLog,即可触发异步逻辑。

2. 拆分异步方法到独立服务类

将writeBatchLog方法迁移到独立的服务类中,比如BatchLogAsyncService:

@Service
public class BatchLogAsyncService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    private static final Logger logger = LoggerFactory.getLogger(BatchLogAsyncService.class);

    @Async
    public void writeBatchLog(int batchNo, String errorTxt) {
        MapSqlParameterSource namedParameters = new MapSqlParameterSource();
        namedParameters.addValue("batchNo", batchNo);
        namedParameters.addValue("error", errorTxt);
        try {
            jdbcTemplate.update("INSERT INTO batches_error_log (error_log, batch_no) values (:error, :batchNo)", namedParameters);
        } catch (Exception e) {
            logger.error("writing into batchLog {} was unsuccessful", batchNo);
        }
    }
}

然后在原服务类中注入这个新服务,通过代理对象调用:

@Service
public class BatchLogGateway {
    @Autowired
    private BatchLogAsyncService batchLogAsyncService;

    public void someInternalMethod(int batchNo, String errorTxt) {
        batchLogAsyncService.writeBatchLog(batchNo, errorTxt);
    }
}

3. 利用AopContext获取当前代理

先修改配置类,开启暴露代理:

@Configuration
@EnableAsync
@EnableAspectJAutoProxy(exposeProxy = true)
public class ThreadConfig {
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(60);
        executor.setMaxPoolSize(200);
        executor.setThreadNamePrefix("ASYNC-");
        executor.initialize();
        return executor;
    }
}

然后在服务内部调用时,通过AopContext获取代理对象:

@Service
public class BatchLogGateway {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    private static final Logger logger = LoggerFactory.getLogger(BatchLogGateway.class);

    public void someInternalMethod(int batchNo, String errorTxt) {
        BatchLogGateway proxy = (BatchLogGateway) AopContext.currentProxy();
        proxy.writeBatchLog(batchNo, errorTxt);
    }

    @Async
    public void writeBatchLog(int batchNo, String errorTxt) {
        MapSqlParameterSource namedParameters = new MapSqlParameterSource();
        namedParameters.addValue("batchNo", batchNo);
        namedParameters.addValue("error", errorTxt);
        try {
            jdbcTemplate.update("INSERT INTO batches_error_log (error_log, batch_no) values (:error, :batchNo)", namedParameters);
        } catch (Exception e) {
            logger.error("writing into batchLog {} was unsuccessful", batchNo);
        }
    }
}

验证

执行后查看日志,若出现ASYNC-前缀的线程名,说明异步逻辑已生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 07:26:01