⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 苏三说技术 「苏三」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

在大型电商系统中,数据导出是一个高频且重要的功能需求。

传统的同步导出方式在面对大数据量时往往会导致请求超时、内存溢出等问题,严重影响用户体验。

苏三商城项目设计并实现了一套完整的Excel异步导出机制,通过注解驱动、任务队列、定时调度、消息通知等技术手段,完美解决了大数据量导出的技术难题,成为项目的技术亮点之一。

感兴趣的小伙伴,可以通过文末加入星球,学习完整的项目实战内容。

系统架构设计

整体架构图

核心组件说明

  1. 注解驱动层:通过@ExcelExport注解实现声明式编程
  2. 切面处理层:CommonTaskAspect负责拦截和任务创建
  3. 任务管理层:ExcelExportTask执行具体的导出逻辑
  4. 调度引擎层:基于Quartz的定时任务调度
  5. 消息通知层:RocketMQ + WebSocket实现异步通知
  6. 存储层:MySQL存储任务状态,OSS存储导出文件

异步导出流程详解

完整流程图

关键步骤分析

1. 注解驱动任务创建

@ExcelExport(ExcelBizTypeEnum.USER)
@ApiOperation(notes = "导出用户数据", value = "导出用户数据")
@PostMapping("/export")
public void export(HttpServletResponse response, UserConditionEntity userConditionEntity) {
// 方法体可以为空,切面会自动处理
}

设计亮点:

  • 声明式编程:通过注解实现功能声明,代码简洁
  • 零侵入性:业务方法无需修改,切面自动处理
  • 类型安全:通过枚举确保业务类型的正确性

2. 切面拦截与任务创建

@Aspect
@Component
public class CommonTaskAspect {

@Before("@annotation(cn.net.susan.annotation.ExcelExport)")
public void before(JoinPoint joinPoint) throws Throwable {
// 获取注解信息
ExcelBizTypeEnum excelBizTypeEnum = method.getAnnotation(ExcelExport.class).value();

// 创建任务实体
CommonTaskEntity commonTaskEntity = createCommonTaskEntity(excelBizTypeEnum);

// 保存任务到数据库
commonTaskMapper.insert(commonTaskEntity);
}
}

技术特色:

  • AOP切面编程:实现横切关注点的分离
  • 反射机制:动态获取注解信息和方法参数
  • 任务持久化:将任务信息保存到数据库,确保可靠性

3. 定时任务调度机制

@Component
public class CommonTaskJob extends BaseJob {

@Override
public JobResult doRun(String params) {
// 查询待执行任务
CommonTaskConditionEntity condition = new CommonTaskConditionEntity();
condition.setStatusList(Arrays.asList(
TaskStatusEnum.WAITING.getValue(),
TaskStatusEnum.RUNNING.getValue()
));

List<CommonTaskEntity> tasks = commonTaskMapper.searchByCondition(condition);

// 执行任务
for (CommonTaskEntity task : tasks) {
AsyncTaskStrategyContextFactory.getInstance()
.getStrategy(task.getType())
.doTask(task);
}

return JobResult.SUCCESS;
}
}

核心机制:

  • 定时扫描:通过Quartz定时扫描任务队列
  • 策略模式:根据任务类型选择对应的处理器
  • 并发处理:支持多个任务并发执行

4. 异步任务处理器

@AsyncTask(TaskTypeEnum.EXPORT_EXCEL)
@Service
public class ExcelExportTask implements IAsyncTask {

@Override
public void doTask(CommonTaskEntity commonTaskEntity) {
try {
// 1. 更新任务状态为执行中
commonTaskEntity.setStatus(TaskStatusEnum.RUNNING.getValue());
commonTaskMapper.update(commonTaskEntity);

// 2. 获取业务类型和请求参数
ExcelBizTypeEnum excelBizTypeEnum = getExcelBizTypeEnum(commonTaskEntity.getBizType());
String requestParam = commonTaskEntity.getRequestParam();
Object toBean = JSONUtil.toBean(requestParam, aClass);

// 3. 获取对应的Service并执行导出
String serviceName = this.getServiceName(requestEntity);
BaseService baseService = (BaseService) SpringBeanUtil.getBean(serviceName);
String fileName = getFileName(excelBizTypeEnum.getDesc());
String fileUrl = baseService.export(toBean, fileName, this.getEntityName(requestEntity));

// 4. 更新任务状态为成功
commonTaskEntity.setFileUrl(fileUrl);
commonTaskEntity.setStatus(TaskStatusEnum.SUCCESS.getValue());

} catch (Exception e) {
// 5. 处理失败情况
handleTaskFailure(commonTaskEntity, e);
} finally {
// 6. 更新任务记录并发送通知
commonTaskMapper.update(commonTaskEntity);
sendNotifyMessage(commonTaskEntity);
}
}
}

处理流程:

  • 状态管理:完整的任务状态流转(等待→执行中→成功/失败)
  • 异常处理:完善的异常捕获和失败重试机制
  • 动态调用:通过反射动态获取Service实例
  • 通知机制:任务完成后自动发送通知

5. 消息通知机制

@RocketMQMessageListener(
topic = "${mall.mgt.excelExportTopic:EXCEL_EXPORT_TOPIC}",
consumerGroup = "${mall.mgt.excelExportGroup:EXCEL_EXPORT_GROUP}")
@Component
public class ExcelExportConsumer implements RocketMQListener<MessageExt> {

@Override
public void onMessage(MessageExt message) {
String content = new String(message.getBody());
CommonNotifyEntity commonNotifyEntity = JSONUtil.toBean(content, CommonNotifyEntity.class);
pushNotify(commonNotifyEntity);
}

private void pushNotify(CommonNotifyEntity commonNotifyEntity) {
// 通过WebSocket推送通知
WebSocketServer.sendMessage(commonNotifyEntity);

// 更新通知状态
commonNotifyEntity.setIsPush(1);
commonNotifyMapper.update(commonNotifyEntity);
}
}

通知特色:

  • 异步解耦:通过消息队列实现系统解耦
  • 实时推送:WebSocket确保用户及时收到通知
  • 可靠性保证:消息队列确保通知的可靠传递

技术架构亮点

1. 策略模式 + 工厂模式

public class AsyncTaskStrategyContextFactory {
private static Map<Integer, IAsyncTask> asyncTaskMap;

public IAsyncTask getStrategy(Integer taskType) {
return asyncTaskMap.get(taskType);
}
}

设计优势:

  • 扩展性强:新增任务类型只需实现IAsyncTask接口
  • 维护性好:每种任务类型独立实现,互不影响
  • 配置灵活:通过工厂模式统一管理任务策略

2. 注解驱动编程

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelExport {
ExcelBizTypeEnum value();
}

编程范式:

  • 声明式编程:通过注解声明功能,而非命令式实现
  • 元数据驱动:注解携带的元数据驱动系统行为
  • 代码简洁:业务代码保持简洁,关注点分离

3. 异步任务状态机

状态管理:

  • 状态流转:清晰的状态转换逻辑
  • 重试机制:失败任务自动重试,提高成功率
  • 状态持久化:任务状态持久化到数据库

4. 分页大数据处理

private String doExport(V v, String fileName, String clazzName) {
RequestConditionEntity conditionEntity = (RequestConditionEntity) v;

// 计算分页参数
int totalCount = getBaseMapper().searchCount(conditionEntity);
int sheetCount = totalCount % sheetDataSize == 0 ?
totalCount / sheetDataSize : totalCount / sheetDataSize + 1;

// 创建ExcelWriter
ExcelWriter excelWriter = EasyExcel.write(file).build();

// 分页处理数据
for (int sheetIndex = 1; sheetIndex <= sheetCount; sheetIndex++) {
List<K> dataEntities = getBaseMapper().searchByCondition(conditionEntity);

// 写入数据到Sheet
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet" + sheetIndex)
.head(Class.forName(clazzName)).build();
excelWriter.write(dataEntities, writeSheet);

conditionEntity.setPageNo(conditionEntity.getPageNo() + 1);
}

excelWriter.finish();
return uploadToOSS(file);
}

处理策略:

  • 内存优化:分页查询避免大量数据加载到内存
  • 流式处理:使用EasyExcel的流式API
  • 多Sheet支持:大数据自动分割到多个Sheet
  • 进度可控:分页处理便于监控和中断

技术优势

1. 用户体验优势

  • 即时响应:用户请求后立即返回,无需等待
  • 实时通知:通过WebSocket实时推送导出结果
  • 进度可见:用户可以实时查看导出进度
  • 错误友好:导出失败时提供详细的错误信息

2. 系统性能优势

  • 高并发:异步处理支持高并发导出请求
  • 资源优化:分页处理避免内存溢出
  • 负载均衡:任务队列支持负载均衡
  • 可扩展性:支持水平扩展和垂直扩展

3. 开发维护优势

  • 代码简洁:注解驱动,业务代码简洁
  • 易于扩展:新增业务类型只需添加注解
  • 统一管理:所有导出任务统一管理和监控
  • 错误处理:完善的异常处理和重试机制

4. 运维管理优势

  • 任务监控:完整的任务执行监控
  • 状态管理:清晰的任务状态流转
  • 日志记录:详细的操作日志记录
  • 告警机制:完善的异常告警机制

总结

苏三商城的Excel异步导出机制是一个设计精良、功能完善的企业级解决方案。

它通过以下技术手段实现了高效、稳定、可扩展的异步导出功能:

核心技术栈

  • 注解驱动:@ExcelExport注解实现声明式编程
  • AOP切面:CommonTaskAspect实现横切关注点分离
  • 策略模式:AsyncTaskStrategyContextFactory实现任务策略管理
  • 定时调度:Quartz实现任务定时调度
  • 消息队列:RocketMQ实现异步通知
  • 实时通信:WebSocket实现实时推送
  • 文件存储:OSS实现文件云端存储

设计亮点

  1. 异步解耦:通过任务队列实现请求与处理的解耦
  2. 状态管理:完整的任务状态流转和持久化
  3. 错误处理:完善的异常处理和重试机制
  4. 性能优化:分页处理、流式写入、内存优化
  5. 扩展性强:支持业务类型、导出格式、通知方式的扩展

业务价值

  • 提升用户体验:异步处理避免长时间等待
  • 提高系统性能:支持大数据量导出和高并发请求
  • 降低开发成本:注解驱动减少重复代码
  • 便于运维管理:统一的任务管理和监控

这套异步导出机制不仅解决了传统同步导出的技术难题,还提供了良好的扩展性和维护性,是苏三商城项目的技术亮点之一,值得在其他项目中推广和应用。

文章目录
  1. 1. 系统架构设计
    1. 1.1. 整体架构图
    2. 1.2. 核心组件说明
  2. 2. 异步导出流程详解
    1. 2.1. 完整流程图
    2. 2.2. 关键步骤分析
      1. 2.2.1. 1. 注解驱动任务创建
      2. 2.2.2. 2. 切面拦截与任务创建
      3. 2.2.3. 3. 定时任务调度机制
      4. 2.2.4. 4. 异步任务处理器
      5. 2.2.5. 5. 消息通知机制
  3. 3. 技术架构亮点
    1. 3.1. 1. 策略模式 + 工厂模式
    2. 3.2. 2. 注解驱动编程
    3. 3.3. 3. 异步任务状态机
    4. 3.4. 4. 分页大数据处理
  4. 4. 技术优势
    1. 4.1. 1. 用户体验优势
    2. 4.2. 2. 系统性能优势
    3. 4.3. 3. 开发维护优势
    4. 4.4. 4. 运维管理优势
  5. 5. 总结
    1. 5.1. 核心技术栈
    2. 5.2. 设计亮点
    3. 5.3. 业务价值