首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 調用 .NET DLL 實戰:跨平台互操作方案 (COM + Jacob) 完整實現

     編輯:Java 相關知識

業務場景與技術選型

專案需整合客戶提供的 .NET 加密 DLL,Java 應用無法直接載入非托管/托管混合 DLL。主流跨語言互操作方案對比:

受限於客戶環境 (Windows Server + 既有 DLL 無法重構),採用 COM + Jacob 方案快速交付。

環境準備

  • .NET Framework 3.5+, C# (Visual Studio)
  • JDK 1.8+, Tomcat 8.5+ (目標部署)
  • Jacob 1.18+ (JNI-COM Bridge):SourceForge 下載 jacob.jar + jacob-1.18-x64.dll (64位系統必用 x64 版)。

步驟一:C# 建立 COM 可見組件

using System;
using System.Runtime.InteropServices;

namespace CryptoBridge
{
    // 1. 定義介面 + GUID + DispId
    [Guid("E9BCF867-CD81-40D0-9591-ED28D1ED2B53")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ICrypto
    {
        [DispId(1)]
        string Encrypt(string plainText, string key);
        [DispId(2)]
        string Decrypt(string cipherText, string key);
    }

    // 2. 實現類 + GUID + ClassInterfaceType.None (避免自動生成介面)
    [Guid("33A6E58D-E1F5-4B53-B2E2-03B9F8BA2FAD")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("CryptoBridge.Crypto")] // 友好 ProgID,供 Java 呼叫
    public class Crypto : ICrypto
    {
        // 內部引用客戶加密 DLL
        // private readonly ClientCryptoLib _lib = new ClientCryptoLib(); 

        public string Encrypt(string plainText, string key)
        {
            // return _lib.Encrypt(plainText, key); // 真實調用
            return $"ENCRYPTED|{plainText}|{key}"; // 模擬
        }

        public string Decrypt(string cipherText, string key)
        {
            // return _lib.Decrypt(cipherText, key);
            return $"DECRYPTED|{cipherText}|{key}";
        }
    }
}

關鍵專案設定 (Visual Studio 專案屬性):

  1. Application → Assembly Information: 勾選 Make assembly COM-Visible。
  2. Build: 勾選 Register for COM interop (開發機自動註冊)。
  3. Signing: 勾選 Sign the assembly,新建/選擇強名稱金鑰 (.snk),**GAC 部署必需**。
  4. 編譯產出:CryptoBridge.dll, CryptoBridge.tlb (類型庫)。

生產機手動註冊與 GAC 安裝 (管理員 PowerShell):

# 1. 註冊 COM (生成/更新註冊表)
regasm CryptoBridge.dll /tlb:CryptoBridge.tlb /codebase

# 2. 安裝到 GAC (需強名稱)
gacutil /i CryptoBridge.dll

# 3. 驗證
regasm /l CryptoBridge.dll  # 列出已註冊類型
gacutil /l CryptoBridge     # 確認 GAC 存在

步驟二:Java 整合 Jacob 呼叫 COM

1. 部署 Jacob 原生庫

  • jacob.jar → 專案 Classpath (Maven: `system` scope 或本地私服安裝)。
  • jacob-1.18-x64.dll → **兩處必放**:
    1. C:\Windows\System32\ (系統路徑,JNI 預設搜尋)。
    2. $JAVA_HOME\bin\ (Tomcat/JVM 啟動路徑,**Web 應用必備**),或配置 -Djava.library.path 指向自定義目錄。

2. Java 呼叫代碼

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;

public class CryptoClient {
    // 單例模式管理 COM 物件生命週期 (昂貴資源)
    private static final ActiveXComponent cryptoCom = new ActiveXComponent("CryptoBridge.Crypto");

    public static String encrypt(String plainText, String key) {
        // COM 呼叫需在 STA 執行緒,Jacob 自動處理,但建議顯式管理
        ComThread.InitSTA();
        try {
            Variant result = Dispatch.call(cryptoCom, "Encrypt", plainText, key);
            return result.getString();
        } catch (Exception e) {
            throw new RuntimeException("COM 加密呼叫失敗", e);
        } finally {
            ComThread.Release();
        }
    }

    public static String decrypt(String cipherText, String key) {
        ComThread.InitSTA();
        try {
            Variant result = Dispatch.call(cryptoCom, "Decrypt", cipherText, key);
            return result.getString();
        } catch (Exception e) {
            throw new RuntimeException("COM 解密呼叫失敗", e);
        } finally {
            ComThread.Release();
        }
    }

    // 應用關閉時釋放 (ServletContextListener / Spring DisposableBean)
    public static void shutdown() {
        if (cryptoCom != null) cryptoCom.safeRelease();
        ComThread.Quiesce();
    }
}

坑點總結與避雷指南

  1. 位數匹配: JDK、Jacob DLL、OS、.NET 目標平台 **必須統一 64 位** (現代標準)。32 位 Jacob 無法在 64 位 JVM 載入。
  2. COM 執行緒模型: .NET COM 組件預設 STA (單執行緒單元)。Jacob ComThread.InitSTA() 確保呼叫執行緒進入 STA,**多執行緒環境下必須成對呼叫 InitSTA/Release**,否則死鎖/崩潰。
  3. 資源釋放: ActiveXComponent.safeRelease() + ComThread.Quiesce() 在應用關閉時執行,防止 COM 物件洩漏導致記憶體增長。
  4. 異常轉換: COM 端拋出異常會包裝為 Jacob ComFailException,需捕獲轉換為業務異常,記錄 ex.getErrorCode() (HRESULT) 診斷。
  5. 部署自動化: 將 regasm /gacutil 封裝進安裝腳本 / Dockerfile (Windows 容器) / Ansible Playbook,實現基礎設施即代碼。
  6. 安全性: COM 組件以應用程式池身份運行,**最小權限原則**,嚴禁以 Administrator 運行 Tomcat。

架構演進建議

此方案屬 **戰術性妥協** (Technical Debt)。中長期規劃:將 .NET 加密邏輯遷移至 **獨立微服務** (ASP.NET Core gRPC/REST),Java 端透過 Feign/gRPC Client 呼叫。優勢:去 Windows 綁定、支援容器化/K8s、技術棧解耦、獨立擴縮容、可觀測性完善。Jacob 方案作為過渡期過橋,預留介面抽象 (CryptoService interface),遷移時僅替換實現類。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案原理優點缺點適用場景
COM + Jacob (本文方案).NET 類暴露為 COM 組件,Java 經 Jacob (JNI 橋接) 呼叫 COMWindows 原生支援、.NET 類庫零改動、穩定成熟**僅限 Windows**、部署需註冊 COM、Jacob 維護較舊Windows 部署、Legacy .NET 元件整合
JNI / JNAC++ 封裝 DLL 出口函數,Java 經 JNI/JNA 呼叫跨平台、效能高需編寫 C++ 胶水碼、簽名映射複雜、調試困難高效能、跨平台、C/C++ DLL
HTTP/REST/gRPC 微服務化.NET 封裝為獨立服務,Java 遠程調用解耦徹底、技術棧隔離、獨立擴縮容、雲原生友好引入網路延遲、部署運維複雜度上升、序列化開銷**現代架構首選**、長期演進、異構系統整合
IKVM.NET.NET 字節碼轉 Java 字節碼直接在 JVM 運行 .NET 代碼專案停止維護、泛型/反射/混合模式支援不全歷史遷移參考