Conversation
OldQi
commented
Mar 18, 2026
- 移除7200秒过期和引用计数机制,解决频繁初始化及线程安全问题
- 每线程持有独立SDK实例,实现懒初始化和线程内复用
- 新增closeThreadLocalSdk()和closeAllSdks()接口,用于显式释放资源
- 废弃WxCpConfigStorage中旧的SDK管理方法及相关字段,保留兼容实现
- 更新WxCpMsgAuditServiceImpl,实现ThreadLocal管理SDK实例
- 修改API调用逻辑,移除获取和释放SDK的旧流程,简化调用流程
- 补充单元测试覆盖多线程及线程池场景验证安全性
- 更新相关文档,增加使用示例与注意事项指导正确使用方法
- 移除7200秒过期和引用计数机制,解决频繁初始化及线程安全问题 - 每线程持有独立SDK实例,实现懒初始化和线程内复用 - 新增closeThreadLocalSdk()和closeAllSdks()接口,用于显式释放资源 - 废弃WxCpConfigStorage中旧的SDK管理方法及相关字段,保留兼容实现 - 更新WxCpMsgAuditServiceImpl,实现ThreadLocal管理SDK实例 - 修改API调用逻辑,移除获取和释放SDK的旧流程,简化调用流程 - 补充单元测试覆盖多线程及线程池场景验证安全性 - 更新相关文档,增加使用示例与注意事项指导正确使用方法
- 将获取或初始化SDK的方法替换为直接创建SDK - 简化了SDK的调用流程 - 提高代码清晰度和维护性
- 移除 managedSdks 集合及相关代码,不再统一追踪所有SDK实例 - 采用ThreadLocal懒初始化,线程内部复用SDK实例 - closeAllSdks() 方法改为调用 closeThreadLocalSdk(),只清理当前线程资源 - 保留旧API方法,内部调用统一替换为 getOrInitThreadLocalSdk() - 文档更新,明确多线程和多实例场景下的SDK管理及DestroySdk调用规范 - 删除手动调用 Finance.DestroySdk() 的旧测试代码示例,防止误用造成内存泄漏
This reverts commit add61c1.
|
auggie review |
🤖 Augment PR SummarySummary: This PR refactors the WeCom (企业微信) message-audit SDK lifecycle from a shared/expiry/ref-count model to a per-thread Changes:
Technical Notes: Uses a concurrent set to track created SDK ids for bulk cleanup; callers in thread pools are expected to call 🤖 Was this summary useful? React with 👍 or 👎 |
| @NonNull long timeout) throws Exception { | ||
| // 获取或初始化SDK | ||
| long sdk = this.initSdk(); | ||
| long sdk = this.createSdk(); |
There was a problem hiding this comment.
| } | ||
| @Override | ||
| public void closeAllSdks() { | ||
| managedSdks.forEach(sdk -> { |
| log.info("关闭会话存档SDK,sdk={}", sdk); | ||
| }); | ||
| managedSdks.clear(); | ||
| threadLocalSdk.remove(); |
There was a problem hiding this comment.
| if (!targetFile.getParentFile().exists()) { | ||
| targetFile.getParentFile().mkdirs(); | ||
| File targetFile = new File(targetFilePath); | ||
| if (!targetFile.getParentFile().exists()) { |
There was a problem hiding this comment.
Pull request overview
本次 PR 针对企业微信“会话存档”能力重构原生 Finance SDK 的生命周期管理方式,目标是减少频繁初始化/销毁并降低多线程共享句柄带来的线程安全风险,通过 ThreadLocal 让每个线程持有独立 SDK 实例,并提供显式资源释放接口。
Changes:
- 在
WxCpMsgAuditServiceImpl中引入ThreadLocal<Long>与managedSdks,新增closeThreadLocalSdk()/closeAllSdks(),并将推荐 API(不暴露 sdk)切换为线程内复用模式。 - 将
WxCpConfigStorage与WxCpDefaultConfigImpl中旧的“SDK缓存/过期/引用计数”相关字段与方法标记为@Deprecated以保持兼容。 - 调整测试用例示例,强调线程池场景需在 finally 中调用
closeThreadLocalSdk();新增一份设计说明文档。
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java | 用 ThreadLocal 管理会话存档 SDK,新增显式关闭接口并简化推荐 API 调用链 |
| weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java | 对外暴露 closeThreadLocalSdk()/closeAllSdks() 生命周期接口 |
| weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java | 废弃旧的 SDK 缓存/过期/引用计数 API(保留以兼容) |
| weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java | 废弃旧字段与实现方法,继续提供兼容行为 |
| weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java | 更新示例测试:在 finally 调用 closeThreadLocalSdk() |
| docs/giggly-pondering-turtle.md | 新增 ThreadLocal 生命周期重构方案说明文档 |
| FileOutputStream outputStream = new FileOutputStream(targetFile, true); | ||
| outputStream.write(i); | ||
| outputStream.close(); |
| * 获取会话存档SDK | ||
| * 会话存档SDK初始化后有效期为7200秒,无需每次重新初始化 | ||
| * | ||
| * @return sdk id,如果未初始化或已过期返回0 | ||
| * @deprecated SDK 生命周期已改由 {@link me.chanjar.weixin.cp.api.WxCpMsgAuditService} 内部的 ThreadLocal |
| # 会话存档SDK生命周期重构方案 | ||
|
|
||
| ## Context | ||
|
|
| log.info("关闭会话存档SDK,sdk={}", sdk); | ||
| }); | ||
| managedSdks.clear(); | ||
| threadLocalSdk.remove(); | ||
| } |
| private long getOrInitThreadLocalSdk() throws WxErrorException { | ||
| Long sdk = threadLocalSdk.get(); | ||
| if (sdk != null && sdk > 0) { | ||
| return sdk; |
| public void closeAllSdks() { | ||
| managedSdks.forEach(sdk -> { | ||
| Finance.DestroySdk(sdk); | ||
| log.info("关闭会话存档SDK,sdk={}", sdk); | ||
| }); |
| * 创建并初始化一个新 SDK 实例(私有,只在当前线程无 SDK 时调用)。 | ||
| * Finance.loadingLibraries() 底层依赖 System.load(),JVM 保证同一库不重复加载,多线程并发调用安全。 |
| | `weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java` | 废弃旧SDK管理方法 | | ||
| | `weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java` | 废弃旧字段/方法 | | ||
| | `weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java` | 补充测试 | | ||
| | `docs/CP_MSG_AUDIT_SDK_SAFE_USAGE.md` | 更新文档 | | ||
|
|
| /** | ||
| * 测试新的安全API方法(推荐使用) | ||
| * 这些方法不需要手动管理SDK生命周期,更加安全 | ||
| * 这些方法不需要手动管理SDK生命周期,SDK由框架 ThreadLocal 模式统一管理。 | ||
| * 线程池场景下,在 finally 块中调用 closeThreadLocalSdk() 防止SDK随线程复用泄漏。 | ||
| */ |