企業級 J2EE 應用隨時間演進,往往需要增量式升級而非大爆發式重寫。典型場景:修復某個 EJB 組件缺陷、為 Servlet 添加新字段、升級特定模塊依賴的數據庫結構。核心挑戰:在同一 WebSphere 實例中,同時運行同一應用的多個版本,且版本間組件可選擇性共享或隔離。
WebSphere 類加載器層次:
Bootstrap ClassLoader
│
▼
Extension ClassLoader
│
▼
Application ClassLoader (每個 EAR 一個)
│
├── EJB ClassLoader (載入 EJB JAR)
│
└── WAR ClassLoader (載入 Web 模塊,Application 的子加載器)
├── Web Module v1
└── Web Module v2
<!-- application.xml 中為每個版本指定唯一 context-root -->
<module>
<web>
<web-uri>MyBankCMPWebv1.war</web-uri>
<context-root>MyBankCMPWebv1</context-root> <!-- 版本 1 -->
</web>
</module>
<module>
<web>
<web-uri>MyBankCMPWebv2.war</web-uri>
<context-root>MyBankCMPWebv2</context-root> <!-- 版本 2 -->
</web>
</module>
/MyBankCMPWebv1/servlet vs /MyBankCMPWebv2/servlet核心原則:代碼絕不硬編碼 JNDI 名稱,僅引用 java:comp/env/... 邏輯名,部署時綁定到具體物理名。
Context ctx = new InitialContext();
AccountHome home = (AccountHome) PortableRemoteObject.narrow(
ctx.lookup("java:comp/env/ejb/Account"), // 邏輯名
AccountHome.class
);
<!-- ejb-jar.xml 環境項聲明 -->
<session>
<ejb-name>Transfer</ejb-name>
<env-entry>
<env-entry-name>AccountServiceLocatorClassName</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>com.ibm.mybank.service.v1.AccountServiceLocator</env-entry-value>
</env-entry>
</session>
<!-- ibm-ejb-jar-bnd.xmi 綁定到具體 JNDI -->
<ejbBinding xmi:id="..." jndiName="ejb/MyBank/v1/Account"/>
| 組件 | 原始版本 | 版本 1 | 版本 2 |
|---|---|---|---|
| Account EJB | ejb/MyBank/Account | ejb/MyBank/v1/Account | ejb/MyBank/v2/Account |
| Transfer EJB | ejb/MyBank/Transfer | ejb/MyBank/v1/Transfer | ejb/MyBank/v2/Transfer |
java:comp/env/jdbc/MyBankDBjdbc/MyBankDB_v1 / jdbc/MyBankDB_v2com.ibm.mybank.ejb ← 版本無關的基礎接口 (Account, Transfer) com.ibm.mybank.ejb.v1 ← 版本 1 專用實現 (AccountBeanV1, AccountKeyV1) com.ibm.mybank.ejb.v2 ← 版本 2 專用實現 (AccountBeanV2, AccountKeyV2) com.ibm.mybank.service.v1 ← 版本 1 ServiceLocator 實現 com.ibm.mybank.web.v1 ← 版本 1 Web 組件 (CreateAccountServletV1)
Account 接口保持穩定,Transfer 依賴它AccountV2 extends Account 添加 getAccountHolderName()AccountInfoV2 新增字段,舊版本不受影響AccountServiceLocator 接口 + v1/v2 實現MyBank_v1.ear ├── MyBankCMPEJB.jar └── MyBankCMPWeb.war MyBank_v2.ear ├── MyBankCMPEJB.jar └── MyBankCMPWeb.war
EJB 層: MyBankCMPEJB_v1.ear ├── MyBankCMPEJBv1.jar (Account v1 實現) └── MyBankCMPEJBCommonUtility.jar (Transfer EJB + 共享接口/工具類) MyBankCMPEJB_v2.ear ├── MyBankCMPEJBv2.jar (Account v2 實現) └── MyBankCMPEJBCommonUtility.jar (共享,同一份) Web 層: MyBankCMPWeb.ear ├── MyBankCMPWebv1.war (CreateAccount v1) ├── MyBankCMPWebv2.war (CreateAccount v2) └── MyBankCMPWebCommon.war (TransferFunds 等共享 Servlet)
# MyBankCMPEJBv1.jar / META-INF/MANIFEST.MF
Manifest-Version: 1.0
Class-Path: MyBankCMPEJBCommonUtility.jar
# MyBankCMPWebv1.war / META-INF/MANIFEST.MF
Manifest-Version: 1.0
Class-Path: MyBankCMPEJBCommonUtility.jar MyBankCMPEJBv1Utility.jar MyBankCMPWebCommonUtil.jar
Manifest-Version: 1.0
Name: com/ibm/mybank/ejb/v2/
Specification-Title: "EJB"
Specification-Version: "2.0"
Implementation-Title: "MyBank EJBs"
Implementation-Version: "1.4"
public String getImplVersion() {
Package p = this.getClass().getPackage();
return "Server Spec: " + p.getSpecificationTitle() + " " + p.getSpecificationVersion()
+ ", Impl: " + p.getImplementationTitle() + " " + p.getImplementationVersion();
}
// EJB 遠程接口暴露版本信息
public String getVersion() { return getImplVersion(); }
// 客戶端調用檢查兼容性
String serverVersion = accountHome.getVersion();
if (!isCompatible(clientVersion, serverVersion)) {
throw new VersionMismatchException("Client " + clientVersion + " incompatible with server " + serverVersion);
}
serialVersionUID 穩定readObject/writeObject 處理版本差異| 維度 | 關鍵動作 | 工具/配置 |
|---|---|---|
| 類加載 | 拆分 EAR,利用 Application/WAR ClassLoader 隔離 | application.xml, Manifest Class-Path |
| Web 路徑 | 每版本獨立 context-root | web.xml / application.xml |
| JNDI | 代碼用 java:comp/env,部署綁定版本化物理名 | ejb-jar.xml, ibm-ejb-jar-bnd.xmi |
| 外部資源 | DataSource 版本化,環境項綁定 | 資源引用, JNDI 綁定 |
| 接口設計 | 基礎接口穩定,版本接口擴展,包隔離 | Java 包命名規範 |
| 封裝 | 公共模塊獨立 JAR,版本模塊獨立 EAR | Maven 多模塊, Manifest |
| 版本追蹤 | Manifest 版本屬性 + 運行時檢查 | Package.getPackage(), 遠程接口 |
| 序列化 | serialver 工具, 單元測試 |
掌握這套體系,讓你的 J2EE 應用實現平滑演進、灰度發布、版本回滾,真正做到「增量升級,業務零中斷」。