許多企業擁有成熟的 Java 業務邏輯庫(打包為 JAR),重寫成本高、風險大。Ruby Java Bridge (RJB) 允許在 Ruby on Rails 應用中直接載入 JAR、實例化 Java 類、呼叫方法,實現零成本遷移與共存。
| 組件 | 版本要求 | 備註 |
|---|---|---|
| Java SDK | JDK 8+ | 設定 JAVA_HOME |
| Ruby | 2.5+ | 建議使用 RVM/rbenv 管理 |
| Rails | 5.2+ | |
| 作業系統 | Linux / macOS / Windows | Linux 生產環境首選 |
# 1. 設定環境變數(以 Linux 為例)
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$JAVA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64:$JAVA_HOME/jre/lib/amd64/server:$LD_LIBRARY_PATH
# 2. 安裝 RJB Gem
gem install rjb
# 3. 驗證安裝
irb -r rjb -e "puts 'RJB loaded successfully'"
# config/initializers/rjb.rb
require 'rjb'
# 載入 JVM,指定類路徑
Rjb::load(
classpath: '.:/app/lib/legacy-business.jar:/app/lib/third-party.jar',
jvmargs: ['-Xms256m', '-Xmx512m', '-Dfile.encoding=UTF-8']
)
# app/services/legacy_integration_service.rb
class LegacyIntegrationService
# 導入 Java 類到 Ruby 常量
TarArchive = Rjb::import('com.ice.tar.TarArchive')
FileInputStream = Rjb::import('java.io.FileInputStream')
File = Rjb::import('java.io.File')
def self.extract_tar(tar_path, dest_dir)
# 實例化:使用 new_with_sig 指定建構子簽名
file_inst = File.new_with_sig('Ljava.lang.String;', dest_dir)
fis_inst = FileInputStream.new_with_sig('Ljava.lang.String;', tar_path)
archive_inst = TarArchive.new_with_sig('Ljava.io.InputStream;', fis_inst)
# 呼叫方法解壓
archive_inst.extractContents(file_inst)
Rails.logger.info "解壓完成: #{tar_path} -> #{dest_dir}"
rescue => e
Rails.logger.error "RJB 呼叫失敗: #{e.message}"
raise
end
end
| 方法 | 用途 | 示例 |
|---|---|---|
Rjb::import('pkg.Class') | 載入 Java 類到 Ruby 變數 | List = Rjb::import('java.util.ArrayList') |
Class.new_with_sig('sig', args...) | 呼叫帶參數的建構子 | list = List.new_with_sig('(I)V', 10) |
obj.method_name(args) | 直接呼叫非重載方法 | list.add('item') |
obj._invoke('method', 'sig', args) | 呼叫重載方法(指定簽名) | list._invoke('add', '(ILjava/lang/Object;)V', 0, 'first') |
obj._classname | 獲取對象真實 Java 類名 | list._classname # => "java.util.ArrayList" |
基本類型: B=byte, C=char, D=double, F=float, I=int, J=long, S=short, Z=boolean, V=void 引用類型: L全限定類名; 例如 Ljava.lang.String; 陣列: [I = int[], [[Ljava.lang.String; = String[][] 方法簽名: (參數類型...)返回類型 例如 (ILjava/lang/String;)V
| 問題 | 原因 | 解決 |
|---|---|---|
UnsatisfiedLinkError | LD_LIBRARY_PATH 缺少 libjvm.so | 補全 $JAVA_HOME/jre/lib/arch 及 server 目錄 |
| 類找不到 | classpath 路徑錯誤或 JAR 缺失 | 使用絕對路徑,檢查 JAR 內部結構 jar tf |
| 中文亂碼 | JVM 預設編碼非 UTF-8 | 啟動參數加 -Dfile.encoding=UTF-8 |
| 線程安全 | RJB 非線程安全 | 使用 Mutex 同步或連接池模式 |
| 記憶體洩漏 | Java 對象未釋放 | 定期重啟 Worker / 使用 JRuby 替代 |
| 維度 | RJB | JRuby |
|---|---|---|
| 架構 | MRI Ruby + JNI 呼叫 JVM | Ruby 運行在 JVM 之上 |
| 性能 | 跨邊界呼叫有開銷 | 原生 Java 性能,但 Ruby 代碼較慢 |
| 遷移成本 | 極低,現有 Rails 無需改動 | 高,需遷移整個應用到 JRuby |
| Gem 相容性 | 完美支援所有 C Extension Gem | 僅支援 Pure Ruby / Java Gem |
| 適用場景 | 少量 Java 呼叫、快速整合 | 大量 Java 互操作、全棧 JVM 化 |
RJB 為 Ruby on Rails 應用提供了一條低風險、低成本的 Java 遺留資產複用路徑。通過規範的初始化配置、類型簽名管理、異常處理與線程同步策略,可在生產環境穩定運行多年。當業務演進至需大規模 Java 互操作時,再評估遷移至 JRuby 或微服務拆分不晚。