在 WebLogic (10.3.x / 12c / 14c) 部署使用 Hibernate 3/4/5 的應用時,極高概率遇到 ClassNotFoundException: org.hibernate.hql.ast.HqlToken 或 NoSuchMethodError 等 Antlr 相關異常。根因是 WebLogic 系統類加載器優先加載自帶的舊版 Antlr (2.7.6/2.7.7),導致 Hibernate 自帶的新版 Antlr 無法被應用類加載器加載,進而無法找到 Hibernate 專用的 Token 類。本文深度解析類加載機制,提供兩種徹底解決方案。
org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken
at org.hibernate.hql.ast.HqlLexer.panic(HqlLexer.java:57)
at antlr.CharScanner.setTokenObjectClass(CharScanner.java:340)
at org.hibernate.hql.ast.HqlLexer.setTokenObjectClass(HqlLexer.java:31)
...
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:352)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
關鍵調用鏈:HqlLexer.setTokenObjectClass() → CharScanner.setTokenObjectClass() → Utils.loadClass() → 類加載器載入失敗。
Bootstrap ClassLoader (JDK 核心類)
↓
WebLogic System ClassLoader (weblogic.jar, modules/antlr.jar) ← 系統級 Antlr
↓
Application ClassLoader (WEB-INF/lib/hibernate.jar, antlr-2.7.7.jar) ← 應用級 Antlr
HqlLexer 調用父類 CharScanner.setTokenObjectClass("org.hibernate.hql.ast.HqlToken")。CharScanner 屬於 Antlr 包。WebLogic 系統類加載器已加載系統級 antlr.CharScanner。Utils.loadClass() 內部邏輯:優先用 Thread.currentThread().getContextClassLoader() (WebLogic 執行線程上下文類加載器,通常指向系統類加載器) 或 Class.forName() (調用者 CharScanner 的類加載器,即系統類加載器)。org.hibernate.hql.ast.HqlToken (該類在 Hibernate jar 中),拋出 ClassNotFoundException。通過 weblogic-application.xml (EAR) 或 WEB-INF/weblogic.xml (WAR) 配置 <prefer-application-packages>,強制指定包由應用類加載器加載,打破雙親委派優先父加載器的默認行為。
WEB-INF/weblogic.xml)<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app xmlns="https://xmlns.oracle.com/weblogic/weblogic-web-app">
<container-descriptor>
<prefer-application-packages>
<!-- 強制應用加載 Antlr 包 -->
<package-name>antlr.*</package-name>
<!-- Hibernate 核心包 (防止其他衝突) -->
<package-name>org.hibernate.*</package-name>
<!-- 常見衝突庫 -->
<package-name>com.fasterxml.jackson.*</package-name>
<package-name>org.slf4j.*</package-name>
<package-name>javax.persistence.*</package-name>
</prefer-application-packages>
<!-- 可選:優先使用應用類加載器 (WebLogic 10.3.4+) -->
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
</weblogic-web-app>
META-INF/weblogic-application.xml)<prefer-application-packages>
<package-name>antlr.*</package-name>
<package-name>org.hibernate.*</package-name>
</prefer-application-packages>
<PreferApplicationPackages for antlr.*>。優勢:零代碼修改,標準 WebLogic 機制,升級維護友好。
局限:極少數情況下 (OSGi 模塊、共享庫複雜依賴) 可能不生效,需方案二。
直接修改 org.hibernate.hql.ast.HqlLexer,不再通過 Antlr 的 Utils.loadClass() 反射加載 Token 類,而是直接引用 HqlToken.class 對象,繞過類加載器查找。
org.hibernate.hql.ast.HqlLexerpublic class HqlLexer extends HqlBaseLexer {
public void setTokenObjectClass(String tokenClassName) {
super.setTokenObjectClass(HqlToken.class.getName()); // 觸發反射加載
}
}
public class HqlLexer extends HqlBaseLexer {
public void setTokenObjectClass(String tokenClassName) {
// 直接賦值 Class 對象,繞過 CharScanner.setTokenObjectClass -> Utils.loadClass
this.tokenObjectClass = HqlToken.class;
}
}
hibernate-core-5.1.0.Final-sources.jar)。org/hibernate/hql/ast/HqlLexer.java。javac -cp "antlr.jar:hibernate-core.jar" HqlLexer.java。org/hibernate/hql/ast/HqlLexer.class 打入項目 WEB-INF/classes 或重新打包覆蓋 hibernate-core.jar (推薦前者,類加載優先級更高)。原理:CharScanner.tokenObjectClass 是 Class 類型字段。父類 setTokenObjectClass(String) 內部調用 Utils.loadClass(String) 將字符串轉為 Class 再賦值。子類重寫直接賦值 HqlToken.class (編譯期常量,由加載 HqlLexer 的類加載器解析,即應用類加載器),徹底繞過 Antlr 的類加載邏輯。
| 維度 | 方案一: weblogic.xml 過濾 | 方案二: 源碼修改 |
|---|---|---|
| 實施成本 | 極低 (配置文件) | 中等 (編譯、打包、維護補丁) |
| 生效率 | 95%+ (極少數類加載器隔離場景失效) | 100% (原理層面徹底解決) |
| 維護性 | 優 (標準配置,升級無感) | 差 (每次升級 Hibernate 需重新打補丁) |
| 推薦場景 | 常規 WAR/EAR 部署、團隊標準化治理 | 方案一失效、老項目無法修改配置、極致穩定要求 |
prefer-application-packages 仍有效,但需確保包名精確 (如 antlr.* 包含所有子包)。META-INF/MANIFEST.MF 聲明 Extension-List,應用引用時同樣需配置過濾。org.antlr.v4.runtime.*,衝突包名相應調整為 org.antlr.v4.runtime.*。WebLogic 系統自帶 Antlr 與 Hibernate 自帶 Antlr 衝突是經典的「J2EE 容器類加載器優先級」問題。首選方案一:配置 weblogic.xml 的 prefer-application-packages 包含 antlr.* 和 org.hibernate.*,標準、無侵入、易維護。若方案一失效,**方案二修改 HqlLexer.setTokenObjectClass 直接賦值 HqlToken.class** 為終極殺手鐧,原理上徹底切斷對系統類加載器的依賴。建議團隊將方案一納入「WebLogic 部署規範清單」,新項目上線前必檢。