当前位置: 首页 > web >正文

java每日精进 5.27【异步实现】

第一部分:异步任务实现的逐步解析

项目通过  spring-boot-starter-job 技术组件提供异步任务功能,利用 Spring 的 @Async 注解和线程池执行异步操作。结合 TransmittableThreadLocal 解决上下文传递问题(如多租户上下文)。以下以 API 访问日志模块 的异步记录为例,逐步解析实现过程。

1.1 异步任务实现原理

异步任务实现基于以下步骤:

  1. 启用异步支持:通过 @EnableAsync 启用 Spring 的异步功能,配置线程池。
  2. 上下文传递:使用 TransmittableThreadLocal(TTL)确保异步线程继承主线程的上下文(如租户 ID)。
  3. 添加 @Async 注解:在需要异步执行的方法上添加 @Async,将其提交到线程池执行。
  4. 业务逻辑:异步方法调用核心逻辑(如插入日志),不阻塞主线程。

1.2 代码实现步骤

步骤 1:配置异步支持

在 YudaoAsyncAutoConfiguration 类中,启用异步功能并配置线程池以支持 TransmittableThreadLocal。

// 导入必要的 Spring 注解,用于自动配置和异步支持
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
import com.alibaba.ttl.TtlRunnable;// 使用 @AutoConfiguration 注解,标记此类为 Spring Boot 自动配置类
// 该注解表明此类会被 Spring Boot 的自动配置机制自动加载,优先级高于 @Configuration
@AutoConfiguration
// 使用 @EnableAsync 注解,启用 Spring 应用上下文中的异步方法执行支持
// 允许使用 @Async 注解的方法在独立的线程中运行
@EnableAsync
public class YudaoAsyncAutoConfiguration {// 定义一个 Spring Bean,用于自定义 ThreadPoolTaskExecutor 类型的 bean// @Bean 注解将方法的返回值注册为 Spring 容器管理的 bean// 返回值: BeanPostProcessor 类型的 bean,用于在 bean 初始化时进行自定义处理@Beanpublic BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() {// 返回一个匿名内部类,实现 BeanPostProcessor 接口// BeanPostProcessor 是一个 Spring 扩展点,允许在 bean 生命周期中对其进行自定义处理return new BeanPostProcessor() {// 实现 BeanPostProcessor 接口的 postProcessBeforeInitialization 方法// 该方法在 bean 初始化之前(例如 @PostConstruct 之前)被调用// 用途: 检查 bean 是否为 ThreadPoolTaskExecutor 类型,并为其添加任务装饰器// 输入参数://   - bean: 当前正在处理的 bean 实例(Spring 容器中的任意 bean)//   - beanName: 该 bean 在 Spring 应用上下文中的名称// 返回值: 返回修改后的 bean 实例,或原 bean 实例(如果未修改)// 异常: 如果 bean 处理过程中发生错误,抛出 BeansException@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 判断当前 bean 是否为 ThreadPoolTaskExecutor 类型的实例// 如果不是,则直接返回原 bean,不做任何修改if (!(bean instanceof ThreadPoolTaskExecutor)) {return bean;}// 将 bean 转换为 ThreadPoolTaskExecutor 类型// ThreadPoolTaskExecutor 是 Spring 提供的线程池任务执行器,用于异步任务处理ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;// 为线程池任务执行器设置任务装饰器,使用 Alibaba 的 TtlRunnable// TtlRunnable 是 Alibaba TransmittableThreadLocal 的工具类,用于在异步线程中传递 ThreadLocal 变量// 作用: 确保 ThreadLocal 变量(例如上下文信息)在异步任务中可以被正确传递和访问executor.setTaskDecorator(TtlRunnable::get);// 返回修改后的 ThreadPoolTaskExecutor 实例return executor;}};}
}

解释

  • @EnableAsync:启用 Spring 的异步支持,允许使用 @Async 注解。
  • BeanPostProcessor:拦截 ThreadPoolTaskExecutor bean 的初始化过程,添加 TtlRunnable 装饰器。
  • TransmittableThreadLocal:由 Alibaba 提供的 TTL 库(com.alibaba:transmittable-thread-local)确保线程上下文(如租户 ID)从主线程传递到异步线程。
  • TtlRunnable:TTL 提供的装饰器,将主线程的上下文包装到异步任务中,解决异步执行时上下文丢失问题(如 TenantContextHolder 的租户 ID)。
步骤 2:引入依赖

在 yudao-module-system-infra 模块中,引入 yudao spring-boot-starter-job 依赖以启用异步功能。

<dependency><groupId>cn.iocoder.boot</groupId><artifactId>yudao spring-boot-starter-job</artifactId>
</dependency>

解释

  • 依赖作用:yudao spring-boot-starter-job 提供异步任务和定时任务的支持,包含必要的线程池配置和 TTL 集成。
  • 模块化:yudao 项目将异步功能封装为 starter,方便模块复用。
步骤 3:定义异步接口方法

在 ApiAccessLogApi 接口中,定义异步方法 createApiAccessLogAsync,并添加 @Async 注解。

public interface ApiAccessLogApi {/*** 创建 API 访问日志** @param createDTO 创建信息*/void createApiAccessLog(@Valid ApiAccessLogCreateReqDTO createDTO);/*** 【异步】创建 API 访问日志** @param createDTO 访问日志 DTO*/@Asyncdefault void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {createApiAccessLog(createDTO);}
}

解释

  • createApiAccessLog:同步方法,执行日志插入逻辑。
  • createApiAccessLogAsync:异步方法,通过 @Async 标记,调用同步方法。default 关键字允许在接口中提供默认实现。
  • @Async:指示 Spring 将方法提交到线程池异步执行,调用后立即返回,不阻塞主线程。
步骤 4:实现服务逻辑

在 ApiAccessLogApiImpl 和 ApiAccessLogServiceImpl 类中,实现日志插入逻辑。

@Service
@Validated
public class ApiAccessLogApiImpl implements ApiAccessLogApi {@Resourceprivate ApiAccessLogService apiAccessLogService;@Overridepublic void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {apiAccessLogService.createApiAccessLog(createDTO);}
}
@Service
@Validated
@Slf4j
public class ApiAccessLogServiceImpl implements ApiAccessLogService {@Resourceprivate ApiAccessLogMapper apiAccessLogMapper;@Overridepublic void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);apiAccessLog.setRequestParams(StrUtils.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));apiAccessLog.setResultMsg(StrUtils.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH));if (TenantContextHolder.getTenantId() != null) {apiAccessLogMapper.insert(apiAccessLog);} else {// 忽略租户上下文,避免插入失败TenantUtils.executeIgnore(() -> apiAccessLogMapper.insert(apiAccessLog));}}
}

解释

  • ApiAccessLogApiImpl:实现接口,调用 ApiAccessLogService 的同步方法。
  • ApiAccessLogServiceImpl
    • 转换 DTO 为 DO(ApiAccessLogDO)。
    • 限制请求参数和结果消息的长度(REQUEST_PARAMS_MAX_LENGTH, RESULT_MSG_MAX_LENGTH)。
    • 处理多租户:如果上下文有租户 ID,直接插入;否则使用 TenantUtils.executeIgnore 忽略租户隔离插入日志。
  • 异步调用:业务代码调用 createApiAccessLogAsync,该方法通过 @Async 在线程池中执行 createApiAccessLog,不阻塞主线程。
步骤 5:使用异步方法

在业务逻辑中,调用 ApiAccessLogApi.createApiAccessLogAsync 异步记录日志。

1.3 实现总结

  • 异步支持:通过 @EnableAsync 和 ThreadPoolTaskExecutor 启用异步,配置 TTL 解决上下文传递。
  • 依赖:引入 yudao spring-boot-starter-job,提供线程池和异步功能。
  • 方法定义:在接口中定义异步方法,添加 @Async 注解,调用同步逻辑。
  • 业务逻辑:异步方法插入日志,结合多租户支持,确保数据正确性。
  • 优势:异步记录日志不阻塞主线程,提升系统性能。

http://www.xdnf.cn/news/9440.html

相关文章:

  • C3P0连接池的使用方法和源码分析
  • Linux系统 - 系统编程概念
  • 【Redis】常用的数据类型 + 单线程模型
  • 答疑:鲜羊奶如何助力亲子关系平衡?
  • 全志V853 mpp程序开发
  • Python训练营---Day38
  • Kubernetes 中的CRD(Custom Resource Definition)与Operator详解
  • Web前端入门:JavaScript 运算符 == 和 === 有什么区别?
  • 枪弹库专用门
  • Vue3 封装el-table组件
  • [学习]C语言指针函数与函数指针详解(代码示例)
  • 2025年6月亲测可用 | 剪映免SVIP版本 | 支持数字人
  • esp32 sip voip 软电话
  • 创建型模式之Abstract Factory(抽象工厂)
  • o1 mini vs o3 mini vs o3 mini high:2025全面对比测评(性能/价格/场景)
  • js获取浏览器中文参数
  • 从预测到验证一键get靶基因结合的转录因子
  • 余弦退火:助力模型训练的优化算法
  • 如何通过TDE透明加密保护智慧档案管理系统中的数据
  • 秒杀系统—1.架构设计和方案简介
  • 【Linux】Linux 操作系统 - 19 , 重谈文件(三) ~ 学好 Linux 精髓是什么 , 缓冲区又是什么 ???【面试】
  • 防火墙的访问控制
  • 【第1章 基础知识】1.4 开始学习HTML5
  • FLUX的实时显示
  • leetcode530.二叉搜索树的最小绝对差:递归中序遍历的差值追踪之道
  • t006-艺体培训机构业务管理系统
  • 上讯信息运维管理审计系统imo.php存在命令执行漏洞(CNVD-2025-07703)
  • Java基础打卡-集合2025.05.22
  • NHANES指标推荐:MQI
  • 2025吉林长春CCPC