@@ -52,8 +52,11 @@ Thread C: init SDK_C → ...
5252
5353** 新增字段:**
5454``` java
55- /* * 每个线程持有独立 SDK 实例,懒初始化,线程内跨调用复用 */
55+ /* * 每个线程持有独立SDK实例 */
5656private final ThreadLocal<Long > threadLocalSdk = new ThreadLocal<> ();
57+
58+ /* * 跟踪所有已创建SDK,用于统一清理 */
59+ private final Set<Long > managedSdks = ConcurrentHashMap . newKeySet();
5760```
5861
5962** 废弃字段/方法:**
@@ -74,6 +77,7 @@ private long getOrInitThreadLocalSdk() throws WxErrorException {
7477 }
7578 long newSdk = createSdk();
7679 threadLocalSdk. set(newSdk);
80+ managedSdks. add(newSdk);
7781 log. info(" 线程 [{}] 初始化会话存档SDK成功,sdk={}" , Thread . currentThread(). getName(), newSdk);
7882 return newSdk;
7983}
@@ -95,17 +99,22 @@ public void closeThreadLocalSdk() {
9599 Long sdk = threadLocalSdk. get();
96100 if (sdk != null && sdk > 0 ) {
97101 Finance . DestroySdk(sdk);
102+ managedSdks. remove(sdk);
98103 threadLocalSdk. remove();
99104 log. info(" 线程 [{}] 关闭会话存档SDK,sdk={}" , Thread . currentThread(). getName(), sdk);
100105 }
101106}
102107
103108/**
104- * 等同于 closeThreadLocalSdk(),无法感知其他线程的SDK。
105- * 不再遍历集合销毁所有SDK(已去掉 managedSdks 追踪)。
109+ * 关闭所有线程持有的SDK。应用关闭时调用(如Spring @PreDestroy / Shutdown Hook)。
106110 */
107111public void closeAllSdks() {
108- closeThreadLocalSdk();
112+ managedSdks. forEach(sdk - > {
113+ Finance . DestroySdk(sdk);
114+ log. info(" 关闭会话存档SDK,sdk={}" , sdk);
115+ });
116+ managedSdks. clear();
117+ threadLocalSdk. remove();
109118}
110119```
111120
@@ -114,10 +123,10 @@ public void closeAllSdks() {
114123- 移除 try-finally 中的 ` releaseSdk(sdk) ` 调用(SDK不再每次释放)
115124- 方法变得更简洁:直接使用sdk,无需包装计数
116125
117- ** 保留旧API方法 (getChatDatas / getDecryptData / getChatPlainText / getMediaFile):**
126+ ** 保留旧API方法不变 (getChatDatas / getDecryptData / getChatPlainText / getMediaFile):**
118127- 保持 @Deprecated 标注
119- - ` getChatDatas ` 仅将 ` initSdk() ` 替换为 ` getOrInitThreadLocalSdk() ` (因 initSdk 已移除,必须改),其余逻辑、注释、代码风格完全不动
120- - ` getDecryptData ` / ` getChatPlainText ` / ` getMediaFile ` 两个重载: ** 完全不改动 ** (保留原注释、 ` e.printStackTrace ()` 等)
128+ - 内部调用改为 ` getOrInitThreadLocalSdk() ` 以保持一致性(旧方法也受益于ThreadLocal)
129+ - 移除对 ` initSdk ()` 的依赖
121130
122131### 2. WxCpMsgAuditService(接口新增)
123132
@@ -129,8 +138,8 @@ public void closeAllSdks() {
129138void closeThreadLocalSdk();
130139
131140/**
132- * 等同于 closeThreadLocalSdk(),无法感知其他线程的SDK 。
133- * 适用于应用关闭时(如Spring Bean销毁阶段)清理当前线程资源 。
141+ * 关闭所有会话存档SDK实例 。
142+ * 适用于应用关闭时(如Spring Bean销毁阶段)统一释放资源 。
134143 */
135144void closeAllSdks();
136145```
@@ -182,8 +191,6 @@ try {
1821912 . ** 独立线程无需显式关闭** :线程销毁时JVM会回收,但 ` Finance.DestroySdk() ` 不会自动调用。若关注资源,建议加 finally 清理。
1831923 . ** 多企业(多CorpId)场景** :` threadLocalSdk ` 是实例字段(非static),不同 ` WxCpMsgAuditServiceImpl ` 实例(不同企业)的ThreadLocal独立,互不影响。
1841934 . ** 库加载幂等性** :` Finance.loadingLibraries() ` 底层调用 ` System.load() ` ,JVM保证同一库不重复加载,多线程并发调用安全。
185- 5 . ** 不要手动调用 ` Finance.DestroySdk() ` ** :旧测试代码中的 ` Finance.DestroySdk(chatDatas.getSdk()) ` 会直接销毁 ThreadLocal 管理的 SDK,必须删除。
186- 6 . ** ` closeAllSdks() ` 的局限性** :不追踪所有SDK,只清理当前线程,语义为"尽力清理"。非线程池场景若不调用 ` closeThreadLocalSdk() ` ,原生SDK内存会泄漏到JVM退出,但进程退出后OS会回收。
187194
188195---
189196
0 commit comments