@Async+线程池实现异步操作
Spring 的
@Async
注解结合线程池,可实现方法异步执行,将耗时任务交由后台线程处理,避免阻塞主线程。通过自定义 ThreadPoolTaskExecutor
可灵活控制线程资源,常用于发送通知、日志记录、数据同步等非实时操作场景。-
异步执行:使用 @Async 和自定义线程池(ThreadPoolTaskExecutor)处理异步业务逻辑,确保主线程快速返回。
-
统一异常处理:通过实现 AsyncConfigurer 接口,在 getAsyncUncaughtExceptionHandler() 方法中返回自定义的异常处理器(CustomAsyncExceptionHandler)。该处理器捕获所有未被 try-catch 捕获的异步异常,并将错误日志持久化到 MongoDB,同时记录日志。
-
职责分离:业务方法中仅关注核心逻辑,而异常处理与日志记录均由全局统一机制处理,避免代码重复,提高系统可维护性。
异步线程池配置:AsyncConfig类
/** * 异步线程池配置 */ @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("AsyncExecutor-"); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); } }
自定义异步异常处理器:CustomAsyncExceptionHandler
/** * 自定义异步异常处理器 */ @Component public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(CustomAsyncExceptionHandler.class); @Autowired private MongoTemplate mongoTemplate; @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { // 构建错误日志对象 AsyncErrorLog errorLog = new AsyncErrorLog(); errorLog.setMethodName(method.getName()); errorLog.setParameters(Arrays.toString(params)); errorLog.setExceptionMessage(ex.getMessage()); errorLog.setStackTrace(ExceptionUtils.getStackTrace(ex)); errorLog.setTimestamp(LocalDateTime.now()); // 保存至 MongoDB try { mongoTemplate.save(errorLog, "async_error_logs"); } catch (Exception mongoEx) { logger.error("保存异步错误日志到 MongoDB 失败:{}", mongoEx.getMessage(), mongoEx); } // 记录日志 logger.error("异步方法 '{}' 发生异常:{}", method.getName(), ex.getMessage(), ex); } }
异步服务类:AsyncService
@Service public class AsyncService { private static final Logger logger = LoggerFactory.getLogger(AsyncService.class); /** * 异步任务示例方法,使用自定义线程池 "taskExecutor" 执行。 * 此方法内部模拟了耗时操作和可能发生的异常,用于验证全局异常处理机制。 */ @Async("taskExecutor") public void performAsyncTask() { logger.info("【开始执行异步任务】当前线程:{}", Thread.currentThread().getName()); // 异步执行 todo logger.info("【异步任务执行完毕】当前线程:{}", Thread.currentThread().getName()); } }