当 @Transactional 不再可靠:批量任务中的编程式事务实践
前言Transactional是 Spring 里最常用的注解之一方法上一挂事务就有了简单到几乎不用思考。但它的简单是有前提的——一旦脱离了外部调用一个 public 方法、异常自然向上抛这个理想模型它就可能悄悄失效而且不报错、不提示只在数据里留下一堆对不上的脏记录。本文以一个真实场景为背景异步批量任务里声明式事务失效最终改用PlatformTransactionManager编程式事务解决。全文配一个可以直接跑的 Spring Boot MySQL demo并附上真实的运行日志不是手写伪造的。你可以自己跑一遍亲眼看到事务根本没开启和事务正常回滚两种截然不同的结果。代码和场景都已抽离成通用 demo不涉及任何具体业务。一、场景批量注册用户两张表要原子假设有一个批量注册接口一次注册多个用户每个用户要往两张表写数据t_user用户基本信息姓名、手机号t_account账户信息初始余额业务要求有两条看似普通却正好卡在声明式事务的软肋上单个用户的两步写入必须原子——t_user和t_account要么都成功要么都失败。不能出现用户建了、账户没建的脏数据。单个用户失败不能影响其他用户——批量几百上千个用户某一个数据有问题把它记下来继续跑不能因为一个失败就整批回滚。先看一个看起来加了事务的朴素实现。Async// 异步执行避免阻塞请求线程publicvoidbatchRegisterDeclarative(ListUserusers){ListStringerrorListnewArrayList();for(Useruser:users){try{// 内部方法调用this.registerOneDeclarative(user);log.info([声明式] 用户[{}]注册成功,user.getName());}catch(Exceptione){// 业务要求单个失败不影响整体记下来继续errorList.add(user.getName(): e.getMessage());log.warn([声明式] 用户[{}]注册失败{},user.getName(),e.getMessage());}}log.info([声明式] 批量注册结束失败 {} 个{},errorList.size(),errorList);}// 期望t_user 和 t_account 两步原子Transactional(rollbackForException.class)publicvoidregisterOneDeclarative(Useruser){jdbcTemplate.update(INSERT INTO t_user(name, phone) VALUES (?, ?),user.getName(),user.getPhone());// 模拟第二步出错余额为负时抛异常if(user.getBalance()0){thrownewIllegalArgumentException(初始余额不能为负数);}jdbcTemplate.update(INSERT INTO t_account(name, balance) VALUES (?, ?),user.getName(),user.getBalance());}registerOneDeclarative上明明白白挂着Transactional(rollbackFor Exception.class)。直觉上如果第二步抛异常第一步的t_user插入应该被回滚。但真跑起来结果完全不是这样。二、复现事务根本没生效造三个用户让第二个用户 Bob 的余额为负它会在写完t_user、准备写t_account前抛异常ListUserusersArrays.asList(newUser(Alice,13800000001,100),newUser(Bob,13800000002,-50),// 故意让它在第二步失败newUser(Cathy,13800000003,200));如果事务生效预期结果是Alice 和 Cathy 两步都成功Bob 两步都回滚两张表都应该只有 Alice、Cathy 两条。跑一下声明式版本真实日志如下21:24:15.989 INFO [main] DemoRunner - 运行模式declarative 21:24:16.009 INFO [task-1] UserRegisterService - [声明式] 批量注册开始共 3 个用户线程task-1 21:24:16.029 INFO [task-1] UserRegisterService - [声明式] 用户[Alice]注册成功 21:24:16.041 WARN [task-1] UserRegisterService - [声明式] 用户[Bob]注册失败初始余额不能为负数 21:24:16.052 INFO [task-1] UserRegisterService - [声明式] 用户[Cathy]注册成功 21:24:16.052 INFO [task-1] UserRegisterService - [声明式] 批量注册结束失败 1 个[Bob: 初始余额不能为负数] 21:24:18.028 INFO [main] DemoRunner - 最终结果 [declarative] 21:24:18.028 INFO [main] DemoRunner - t_user 共 3 条[Alice, Bob, Cathy] 21:24:18.028 INFO [main] DemoRunner - t_account 共 2 条[Alice, Cathy] 21:24:18.028 ERROR [main] DemoRunner - 数据不一致t_user 有 Bob 但 t_account 没有 —— 第一步写入未回滚脏数据产生看最后三行t_user有 3 条含 Bobt_account只有 2 条。Bob 的t_user插入没有被回滚脏数据实实在在地留在了库里。更能说明问题的是事务管理器的 DEBUG 日志。我把JdbcTransactionManager的日志级别开到 DEBUG然后数了一下这个版本里它输出了多少行 声明式模式下 JdbcTransactionManager 的日志行数预期 0 00 行。也就是说Transactional标注的方法执行时Spring 事务管理器压根没被触发——不是开了事务但没回滚而是事务从头到尾就没开启过。注解形同虚设。为什么会这样这里同时踩中了两个坑。三、声明式事务失效的经典场景Transactional的本质是Spring AOP 代理容器为你的 Bean 生成一个代理对象在调用被Transactional标注的方法前后由代理去开启、提交、回滚事务。这个代理是一切的关键也是一切失效的根源。下面几种是最常见的失效场景本例踩了其中两个。3.1 self-invocation内部方法调用绕过代理这是本例的首要原因。AsyncpublicvoidbatchRegisterDeclarative(ListUserusers){for(Useruser:users){registerOneDeclarative(user);// this.registerOneDeclarative(...)}}registerOneDeclarative(user)等价于this.registerOneDeclarative(user)。这里的this是原始对象不是 Spring 生成的代理对象。事务增强逻辑只织在代理对象上用this直接调用请求根本不经过代理这一层Transactional自然不生效。外部调用走代理事务生效 Controller ──→ [代理对象] ──→ 目标方法 ✅ 事务开启 内部调用this.绕过代理事务失效 目标方法A ──→ this.目标方法B ❌ 直接调用没有代理介入只要是同一个类里一个方法直接调用另一个带Transactional的方法被调用方法上的事务注解一律失效。3.2 Async 新线程事务上下文不跨线程传播这是本例的第二个坑。Spring 事务上下文是通过ThreadLocal绑定在当前线程上的连接、事务状态都存在ThreadLocal里。Async会把方法丢到另一个线程执行新线程的ThreadLocal是空的拿不到任何外层事务上下文。即便你想靠外层方法的事务来兜底只要跨了Async的线程边界事务上下文就断了。3.3 其他常见失效场景顺带一并记住失效场景原因方法不是publicSpring 默认只对 public 方法织入事务增强异常被 catch 吞掉没抛出声明式事务靠异常向上抛触发回滚异常被吃掉事务不知道要回滚抛的是受检异常但没配rollbackFor默认只对RuntimeException和Error回滚受检异常不回滚类没有被 Spring 管理自己 new 的没有代理对象注解无从谈起数据库引擎不支持事务如 MyISAM底层就没有事务能力3.4 本例为什么无解回到我们的场景即使把 self-invocation 和Async都绕过去比如把registerOneDeclarative拆到另一个 Bean、用REQUIRES_NEW还有第三个矛盾绕不开业务要求单个失败不影响整体所以循环体里必须try-catch把异常吃掉、继续下一个用户。而声明式事务恰恰依赖异常向上抛才能回滚——你一 catch回滚的触发条件就没了。这两个需求在声明式事务的模型里是天然冲突的。结论这个场景声明式事务从根上就不合适。我们需要能手动控制事务边界的工具。四、编程式事务PlatformTransactionManager 登场PlatformTransactionManager是 Spring 事务的底层核心接口Transactional内部也是靠它工作的。直接用它就能把事务的开启、提交、回滚完全握在自己手里不依赖代理也不依赖异常传播。三个核心角色PlatformTransactionManager事务管理器负责getTransaction开启、commit提交、rollback回滚。Spring Boot 用了 JDBC 会自动注入它的实现。DefaultTransactionDefinition事务定义描述传播行为、隔离级别等用默认值即可。TransactionStatus一次事务的句柄提交/回滚时把它传回去。注入很简单AutowiredprivatePlatformTransactionManagertransactionManager;改造后的单个注册方法/** * 编程式事务手动开启两步写入成功提交失败回滚后把异常抛回上层。 * 事务边界精确地包住写 t_user 写 t_account这两步。 */publicvoidregisterOneProgrammatic(Useruser){TransactionStatusstatustransactionManager.getTransaction(newDefaultTransactionDefinition());try{jdbcTemplate.update(INSERT INTO t_user(name, phone) VALUES (?, ?),user.getName(),user.getPhone());if(user.getBalance()0){thrownewIllegalArgumentException(初始余额不能为负数);}jdbcTemplate.update(INSERT INTO t_account(name, balance) VALUES (?, ?),user.getName(),user.getBalance());transactionManager.commit(status);// 两步都成功 → 提交}catch(Exceptione){transactionManager.rollback(status);// 任一步失败 → 回滚throwe;// 抛回上层让批量循环记录并继续}}批量循环几乎不变只是调用换成编程式版本AsyncpublicvoidbatchRegisterProgrammatic(ListUserusers){ListStringerrorListnewArrayList();for(Useruser:users){try{registerOneProgrammatic(user);log.info([编程式] 用户[{}]注册成功,user.getName());}catch(Exceptione){errorList.add(user.getName(): e.getMessage());log.warn([编程式] 用户[{}]注册失败{},user.getName(),e.getMessage());}}log.info([编程式] 批量注册结束失败 {} 个{},errorList.size(),errorList);}这里有个关键设计——内层回滚 外层记录的配合内层registerOneProgrammatic保证单个用户两步原子。失败时先 rollback 再把异常抛出去。外层循环catch住异常记进errorList继续下一个用户。两个需求就这样各归其位原子性由内层的编程式事务保证失败继续由外层的 catch 保证不再互相打架。五、验证事务真正生效了同样的三个用户、同样让 Bob 失败跑编程式版本真实日志含事务管理器 DEBUG 日志21:26:50.680 INFO [task-1] UserRegisterService - [编程式] 批量注册开始共 3 个用户线程task-1 --- Alice两步成功提交 --- 21:26:50.683 DEBUG [task-1] JdbcTransactionManager - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 21:26:50.683 DEBUG [task-1] JdbcTransactionManager - Acquired Connection [HikariProxyConnection1064755138 ...] for JDBC transaction 21:26:50.686 DEBUG [task-1] JdbcTransactionManager - Switching JDBC Connection [...] to manual commit 21:26:50.696 DEBUG [task-1] JdbcTransactionManager - Initiating transaction commit 21:26:50.696 DEBUG [task-1] JdbcTransactionManager - Committing JDBC transaction on Connection [...] 21:26:50.701 DEBUG [task-1] JdbcTransactionManager - Releasing JDBC Connection [...] after transaction 21:26:50.701 INFO [task-1] UserRegisterService - [编程式] 用户[Alice]注册成功 --- Bob第二步失败回滚 --- 21:26:50.701 DEBUG [task-1] JdbcTransactionManager - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 21:26:50.701 DEBUG [task-1] JdbcTransactionManager - Acquired Connection [HikariProxyConnection116848456 ...] for JDBC transaction 21:26:50.701 DEBUG [task-1] JdbcTransactionManager - Switching JDBC Connection [...] to manual commit 21:26:50.702 DEBUG [task-1] JdbcTransactionManager - Initiating transaction rollback 21:26:50.702 DEBUG [task-1] JdbcTransactionManager - Rolling back JDBC transaction on Connection [...] 21:26:50.706 DEBUG [task-1] JdbcTransactionManager - Releasing JDBC Connection [...] after transaction 21:26:50.708 WARN [task-1] UserRegisterService - [编程式] 用户[Bob]注册失败初始余额不能为负数 --- Cathy两步成功提交 --- 21:26:50.708 DEBUG [task-1] JdbcTransactionManager - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 21:26:50.709 DEBUG [task-1] JdbcTransactionManager - Initiating transaction commit 21:26:50.721 DEBUG [task-1] JdbcTransactionManager - Committing JDBC transaction on Connection [...] 21:26:50.721 INFO [task-1] UserRegisterService - [编程式] 用户[Cathy]注册成功 21:26:50.721 INFO [task-1] UserRegisterService - [编程式] 批量注册结束失败 1 个[Bob: 初始余额不能为负数] 21:26:52.692 INFO [main] DemoRunner - 最终结果 [programmatic] 21:26:52.692 INFO [main] DemoRunner - t_user 共 2 条[Alice, Cathy] 21:26:52.692 INFO [main] DemoRunner - t_account 共 2 条[Alice, Cathy] 21:26:52.692 INFO [main] DemoRunner - 数据一致两张表用户完全对应失败的用户两步都没写入。日志把事务的一生讲得清清楚楚AliceCreating new transaction→Committing两步落库。BobCreating new transaction→Initiating transaction rollback→Rolling back第一步的t_user插入被回滚一条不留。Cathy又是一个独立事务正常提交。最终t_user和t_account都是 2 条完全一致脏数据消失。三个用户各自开了一个独立事务互不影响——这正是我们要的每条一个独立小事务的语义。对照一下前面数过的日志行数 声明式模式下 JdbcTransactionManager 的日志行数 → 0 编程式模式下 JdbcTransactionManager 的日志行数 → 18从 0 到 18事务从根本没开变成实实在在地开了 3 次、commit 2 次、rollback 1 次。六、声明式 vs 编程式怎么选维度声明式Transactional编程式PlatformTransactionManager使用方式方法上加注解手动 get/commit/rollback代码侵入性低几乎无侵入高要写模板代码事务边界整个方法任意代码块精确可控依赖代理是self-invocation 会失效否依赖异常传播是catch 吞掉就不回滚否自己决定何时回滚适合场景常规单请求业务边界清晰、异常自然上抛批量循环细粒度控制、异步任务、需运行时决定提交/回滚一句话总结选型绝大多数场景用声明式——简单、干净够用。下面这些情况用编程式批量循环里要每条一个独立事务、单条失败继续本文场景Async等跨线程执行的任务需要在同一个方法里根据运行时条件多次开关事务、精确控制回滚时机。七、踩坑清单坑 1rollback 之后一定要把异常处理掉抛出或记录}catch(Exceptione){transactionManager.rollback(status);throwe;// 不要默默吞掉否则上层以为成功了}如果 rollback 后既不抛也不记事务是回滚了但调用方完全不知道这条失败了会当成成功处理比脏数据更难排查。坑 2事务里别塞耗时 IO编程式事务同样占用数据库连接。getTransaction到commit之间会一直持有连接如果中间夹了 OSS 上传、远程接口调用这类耗时操作连接被长时间占用高并发下很容易把连接池耗尽。事务里只放必须原子的 DB 操作IO 挪到事务外。坑 3理解默认的传播行为new DefaultTransactionDefinition()默认传播行为是PROPAGATION_REQUIRED——有外层事务就加入没有就新建。在Async新线程里通常没有外层事务所以每次都是新建一个独立事务正好符合我们每条独立的需求。但如果哪天把它放进一个已有事务的上下文里行为会变需要留意。日志里那行PROPAGATION_REQUIRED,ISOLATION_DEFAULT就是它在说明自己的传播行为和隔离级别。坑 4循环里频繁开关事务的性能每条记录开一个事务意味着频繁地获取连接—提交—释放连接。数据量极大时这个开销不可忽略。如果业务允许可以考虑每 N 条一个事务折中在原子粒度和性能之间取平衡但要清楚这会改变失败回滚的范围一批里失败会回滚整批。八、附完整可运行 DemoDemo 基于 Spring Boot 2.5.3 MySQLJdbcTemplate直连不引入任何 ORM几个文件就能跑。建库MySQL 里执行一次表由程序自动建CREATEDATABASEIFNOTEXISTStx_demoDEFAULTCHARACTERSETutf8mb4;application.yml打开事务 DEBUG 日志是关键否则看不到 commit/rollbackspring:datasource:url:jdbc:mysql://127.0.0.1:3306/tx_demo?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8username:rootpassword:000000driver-class-name:com.mysql.cj.jdbc.Driverlogging:level:org.springframework.jdbc.support.JdbcTransactionManager:DEBUGcom.example.txdemo:DEBUGpattern:console:%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n核心类UserRegisterService前文已给出完整代码包含声明式和编程式两个版本。启动类记得开启异步EnableAsyncSpringBootApplicationpublicclassTxDemoApplication{publicstaticvoidmain(String[]args){SpringApplication.run(TxDemoApplication.class,args);}}运行方式用启动参数切换两个版本亲自对比结果# 复现声明式失效t_user 3 条、t_account 2 条脏数据java-jartx-demo.jar--modedeclarative# 编程式修复两张表都是 2 条数据一致java-jartx-demo.jar--modeprogrammatic强烈建议自己跑一遍——亲眼看到t_user里那条没被回滚的 Bob比读十遍原理都印象深刻。总结Transactional好用但它建立在代理生效 异常上抛这个前提上。一旦遇到 self-invocation、Async跨线程、或者业务上必须 catch 吞异常这个前提就崩了而且崩得悄无声息。一句话记忆Transactional的本质是 AOP 代理 → self-invocation、非 public、自己 new 的对象一律失效声明式事务靠异常上抛触发回滚 → catch 吞掉异常回滚就没了Async换了线程 → 事务上下文基于 ThreadLocal不跨线程传播批量循环要单条独立事务 失败继续 → 声明式天生做不到用编程式编程式事务getTransaction开 → 业务 →commitcatch 里rollback后别忘了把异常抛回去事务里只放必须原子的 DB 操作耗时 IO 一律挪到事务外工具没有好坏只有适不适合。理解Transactional什么时候会失效比记住加了它就有事务重要得多。

相关新闻

ChatGPT写述职PPT大纲失效真相(92%职场人踩中的3个AI提示陷阱)

ChatGPT写述职PPT大纲失效真相(92%职场人踩中的3个AI提示陷阱)

更多请点击: https://codechina.net 第一章:ChatGPT写述职PPT大纲失效的底层归因 当用户向ChatGPT输入“请为我生成一份技术主管岗位的述职PPT大纲”时,模型常返回结构完整但实际不可落地的输出——例如包含“战略协同力提升”“组织熵减实践…

2026/7/12 1:47:28阅读更多 →
越南ERP怎么选?本土商家场景选型指南|北越/中越/南越狭长供应链进销存风控方案

越南ERP怎么选?本土商家场景选型指南|北越/中越/南越狭长供应链进销存风控方案

�� 地域标签:#越南ERP #越南进销存 #北越库存管理 #南越批发ERP #湄公河多仓管控 #越南实体商家数字化 #狭长供应链管理�� 精准适配人群:越南北越/中越/南越本土实体零售商、南北跨区批发贸易商、全越多仓工…

2026/7/12 1:42:27阅读更多 →
PyNVMe3 22.11 在 Ubuntu 20.04 部署实战:3步配置大页内存,规避常见安装失败

PyNVMe3 22.11 在 Ubuntu 20.04 部署实战:3步配置大页内存,规避常见安装失败

PyNVMe3 22.11 在 Ubuntu 20.04 部署实战:3步配置大页内存,规避常见安装失败NVMe SSD测试工具PyNVMe3凭借其用户态驱动和Python API的灵活性,已成为企业级存储测试的新标杆。但在Ubuntu 20.04系统上部署时,大页内存配置往往是第一…

2026/7/12 1:42:27阅读更多 →
高精度数据采集系统设计:MCP3428 ADC与PIC18F86K90应用

高精度数据采集系统设计:MCP3428 ADC与PIC18F86K90应用

1. 项目背景与硬件选型考量在工业测量和实验室环境中,数据采集系统的精度和稳定性直接决定了后续分析的可靠性。传统基于8位或10位ADC的方案已难以满足现代高精度测量需求,这正是我们选择MCP3428搭配PIC18F86K90构建升级版数据采集系统的核心原因。MCP34…

2026/7/12 2:42:31阅读更多 →
ECharts 5.x 坐标轴样式深度定制:3类属性与5个实战场景解析

ECharts 5.x 坐标轴样式深度定制:3类属性与5个实战场景解析

ECharts 5.x 坐标轴样式深度定制:3类属性与5个实战场景解析在数据可视化领域,ECharts 作为一款强大的 JavaScript 图表库,其灵活的配置项让开发者能够创建出高度定制化的图表。坐标轴作为图表的基础元素,直接影响着数据的呈现效果…

2026/7/12 2:42:31阅读更多 →
PDF补丁丁:5大核心功能帮你轻松搞定PDF处理难题

PDF补丁丁:5大核心功能帮你轻松搞定PDF处理难题

PDF补丁丁:5大核心功能帮你轻松搞定PDF处理难题 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目地址: https://gitcode.…

2026/7/12 2:42:31阅读更多 →
Knife4j 3.0.3 高级配置实战:5个技巧优化 API 文档安全性与可读性

Knife4j 3.0.3 高级配置实战:5个技巧优化 API 文档安全性与可读性

Knife4j 3.0.3 高级配置实战:5个技巧优化 API 文档安全性与可读性在当今微服务架构盛行的时代,API 文档作为前后端协作的桥梁,其质量直接影响着开发效率和系统安全性。Knife4j 作为 Swagger 的增强解决方案,在 Java 生态中广受欢迎…

2026/7/12 2:42:31阅读更多 →
1.1-引言

1.1-引言

机器学习到底在研究什么? 本教程用尽量直白的方式,把「机器学习是什么」从头到尾讲清楚,不需要任何前置资料。读完你就能用大白话给别人解释:为什么计算机能「凭经验」做判断。 目录 一、先搞懂:我们凭什么能「预判」…

2026/7/12 2:42:31阅读更多 →
C++视觉算法优化实战:8大手法提升图像处理性能

C++视觉算法优化实战:8大手法提升图像处理性能

1. 项目概述:为什么C优化是视觉算法的命脉?如果你是一名视觉算法工程师,或者正在向这个方向发展,你一定经历过这样的场景:算法模型在论文里跑分很高,但一到实际工程部署,尤其是在资源受限的嵌入…

2026/7/12 2:37:31阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/12 0:02:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/12 0:02:11阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/12 0:02:11阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/12 0:02:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/12 0:02:11阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/12 0:02:11阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/11 16:20:28阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/11 23:15:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/11 18:12:23阅读更多 →