# example **Repository Path**: yiynx/example ## Basic Information - **Project Name**: example - **Description**: 代码使用示例: ExcelUtil(基于EasyExcel)、 BaseService(MyBatis-Plus)、 ParallelUtil(并行生产数据,串行消费数据[有序]) Xif(策略模式工具包,通过注解实现策略模式) - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 45 - **Forks**: 39 - **Created**: 2022-10-30 - **Last Updated**: 2026-01-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 示例项目 ## 项目概述 这是一个 Java 21/Spring Boot 3 示例项目。该项目展示了: - **ExcelUtil**(基于 EasyExcel)用于 Excel 导入/导出操作,对导出性能做出了优化,支持并发&流式查询导出。 - **MyBatis-Plus** 自定义 BaseService & BaseServiceImpl 实现 - **ParallelUtil、SlidingWindow** - 并发工具类,用于并发生产数据,有序串行消费数据 - **Xif** - 使用注解的策略模式实用程序包 - **Google @AutoService** 注解实现的策略模式示例 - **Lombok @ExtensionMethod** 扩展方法+注解示例 - **Javamelody** 监控集成 ## 技术栈 - Java 21 - Spring Boot 3 - MySQL/PostgreSQL - EasyExcel - MyBatis-Plus - Maven - Hutool - Lombok - Javamelody - Xif(自定义策略模式库) - Google AutoService ## 构建和开发命令 **构建项目:** ```bash mvn clean install ``` **运行测试:** ```bash mvn test ``` **运行应用程序:** ```bash mvn spring-boot:run ``` **打包部署:** ```bash mvn clean package ``` **运行特定测试类:** ```bash mvn test -Dtest=ParallelUtilTest ``` ## 先决条件 - Java 21 - Maven - MySQL(或 PostgreSQL) - 已安装 Lombok 插件的 IDE ## 安装教程 1. 配置 `application.yml` 中的数据库用户名、密码和 schema ```yaml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: ${DB_USERNAME} password: ${DB_PASSWORD} ``` 2. 执行项目 `src/main/resources/db` 目录下的 `mysql.sql` 或 `pgsql.sql` 创建 demo 表,生成测试数据 3. 启动项目 (ExampleApplication) ## 项目架构 项目遵循标准的 Spring Boot 分层架构: ### 自定义 BaseService 模式 `BaseService` 和 `BaseServiceImpl` 扩展了 MyBatis-Plus 的 ServiceImpl 以添加 Excel 导出功能。主要方法包括: - **同步导出**: 标准单线程查询和写入 - **并发导出**: 多线程查询与有序消费 - **流式导出**: 基于 MyBatis-Plus 游标的大型数据集处理 此模式在 `service/DemoService` 中使用,它扩展了 `BaseServiceImpl`。 ### 并发工具 **ParallelUtil、SlidingWindow**: 生产者-消费者模式,用于并行数据生成与有序消费。 ### 策略模式 提供了两种实现: 1. **Xif** (外部库 `cn.yiynx:xif`): 基于注解的策略模式。 2. **AutoService** (Google 的 `@AutoService`): 基于 ServiceLoader 的策略模式 ### Excel 操作 提供多种导出策略: - `exportExcel()`: 同步查询导出 - `writeExcelForParallel()`: 并发查询导出 - `writeExcelForFetch()`: 流式查询导出 ### 扩展方法 项目使用 Lombok 的 `@ExtensionMethod` 实现类似 Kotlin语言 中的扩展方法: - `$also`, `$apply`, `$let`, `$run` - 函数式编程助手(参考Kotlin语言相关方法) - `$format` - 字符串格式化 - `$eq`, `$ne`, `$gt`, `$lt`, `$ge`, `$le` - 安全比较运算符 - `$convert`, `$convertList`, `$convertPage` - 类型转换实用程序 - `$parallelEach` - 并发列表处理(虚拟线程VirtualThread) #### 扩展方法示例对比 以下是一些扩展方法的使用示例,展示了使用扩展方法与传统方法的区别和优势: **1. 字符串格式化 ($format)** **不使用扩展方法:** ```java // 使用String.format进行格式化 String result = String.format("%s%s", 123, "456"); ``` **使用扩展方法:** ```java // 使用扩展方法进行字符串格式化 String result = "{}{}".format(123, "456"); // 使用命名参数进行格式化 String result2 = "{int}{str}".$format(Map.of("int", 123, "str", "456")); ``` **优势:** 使用扩展方法可以直接在对象上调用方法,代码更直观,无需创建额外的工具类调用。 **2. 对象安全操作 ($apply, $let)** **不使用扩展方法:** ```java Demo demo = new Demo(); demo.setId(123); demo.setTitle("abc"); demo.setContent(" 456 "); // 如果要进行链式操作,需要额外的步骤 String upperTitle = null; if (demo != null && demo.getTitle() != null) { upperTitle = demo.getTitle().toUpperCase(); } ``` **使用扩展方法:** ```java // 使用$apply进行对象属性设置 Demo demo = new Demo().$apply(it -> { it.setId(123); it.setTitle("abc"); it.setContent(" 456 "); }); // 使用$let进行安全链式操作 String upperTitle = demo.$let(Demo::getTitle).$let(String::toUpperCase); ``` **优势:** 使用扩展方法可以实现安全的链式调用,避免空指针异常,代码更简洁。 **3. 对象转换 ($convert, $convertList)** **不使用扩展方法:** ```java // 转换对象 DemoVO demoVO = BeanUtil.copyProperties(demo, DemoVO.class); // 批量转换列表 List demoVOList = BeanUtil.copyToList(demoList, DemoVO.class); ``` **使用扩展方法:** ```java // 使用$convert直接转换对象 DemoVO demoVO = demo.$convert(DemoVO.class); // 批量转换列表 List demoVOList = demoList.$convertList(DemoVO.class); ``` **优势:** 使用扩展方法可以一键完成对象转换,减少样板代码,提高开发效率。 **4. 集合操作 ($convertMap, $groupingBy)** **不使用扩展方法:** ```java // 转换为Map Map demoMap = demoList.stream().collect(Collectors.toMap(Demo::getId, Function.identity())); Map> groupMap = demoList.stream().collect(Collectors.groupingBy(Demo::getType)); ``` **使用扩展方法:** ```java // 使用$convertMap转换为Map Map demoMap = demoList.$convertMap(Demo::getId, Function.identity()); // 使用$groupingBy进行分组 Map> groupMap = demoList.$groupingBy(Demo::getType); ``` **优势:** 使用扩展方法可以更简洁地进行集合操作,减少循环和条件判断代码。 **5. 日期格式化 ($format)** **不使用扩展方法:** ```java String formattedDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); ``` **使用扩展方法:** ```java String formattedDate = LocalDateTime.now().$format("yyyy-MM-dd HH:mm:ss"); ``` **优势:** 使用扩展方法可以直接在日期对象上调用格式化方法,语法更简洁。 **6. 并发处理 (虚拟线程:$parallelEach)** **不使用扩展方法:** ```java // 手动创建线程池进行并发处理 try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) { List> futures = demoList.stream() .map(it -> CompletableFuture.runAsync(() -> process(it), executorService)) .toList(); CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } ``` **使用扩展方法:** ```java demoList.$parallelEach(demo -> { process(demo); // 执行某些操作 }); ``` **优势:** 使用扩展方法可以更简洁地进行并发处理(虚拟线程),减少样板代码,提高开发效率。但需要注意ThreadLocal变量的传递问题。 这些扩展方法借鉴了Kotlin等现代语言的特性,使Java代码更简洁、更安全、更具表现力。 ## 使用说明 ### 1. 测试 Excel 导入与导出 - 同步查询版:`GET http://localhost:8080/exportExcel` - 并发查询版1:`GET http://localhost:8080/writeExcelForParallel` - 并发查询版2:`GET http://localhost:8080/writeExcelForXParallel` - 流式查询版:`GET http://localhost:8080/writeExcelForFetch` - 下载导入模板:`GET http://localhost:8080/downloadImportTemplate` - 导入 Excel:`POST http://localhost:8080/importExcel` (form-data key:file -> value:excelFile) ### 2. 测试 Xif(基于注解的策略模式) - `GET http://localhost:8080/xif?type=1` 或 `type=2` 或其他值 ### 3. 监控 - Javamelody 监控:`GET http://localhost:8080/monitoring` ## 重要注意事项 - **IDE 插件**: 需要安装 Lombok 插件 - **Javamelody**: 在 `/monitoring` 端点提供应用程序监控 - **数据库**: 支持 MySQL 和 PostgreSQL(可在 pom.xml 中切换,且需修改mybatisPlusInterceptor分页插件配置为POSTGRE_SQL) - **并发处理**: ParallelUtil 使用有序消费模式 - 数据并行生成但按顺序写入以保持 Excel 行顺序 - **参数验证**: 某些端点暴露了如 `pageSize` 等参数,在生产环境中应进行验证以防止内存耗尽攻击 - **Java 版本**: 由于在并行处理工具中使用了虚拟线程,该项目需要 Java 21 ## 代码结构 项目遵循标准的 Spring Boot 结构: ``` src/ ├── main/ │ ├── java/cn/yiynx/example/ │ │ ├── controller/ # REST 控制器 │ │ ├── entity/ # 实体类 │ │ ├── mapper/ # MyBatis 映射器 │ │ ├── service/ # 业务服务 │ │ ├── util/ # 工具类 │ │ │ ├── dp/ # 策略模式工具 │ │ │ ├── excel/ # Excel 工具 │ │ │ ├── extensions/ # Lombok 扩展 │ │ │ ├── limiter/ # 并发工具类,用于并发生产数据,有序串行消费数据 │ │ │ └── paralle/ # 并发工具类,用于并发生产数据,有序串行消费数据 │ │ ├── vo/ # VO对象 │ │ └── xif/ # Xif 策略模式处理器 │ └── resources/ │ ├── application.yml # 配置 │ └── db/ # 数据库脚本 └── test/ └── java/ # 测试类 ``` ## 主要变更日志 * 2023-03-05 引入Manifold。 * 2023-03-05 Java8->Java17、Spring boot 2.x -> 3.x * 2023-10-25 Java17->Java21、引入javamelody监控 * 2024-02-19 mybatis-plus升级到3.5.5(流式查询版改为使用mybatis-plus流式查询) * 2026-01-07 移除Manifold,改用lombok的扩展方法 ## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request