Java 類載入器按 CLASSPATH 順序搜尋,**第一個同名類別即被載入,後續同名類別被「隱藏」**。當專案依賴增長、多模組合併、第三方 JAR 內含相同包名類別時,常出現:
NoSuchMethodError/ClassCastExceptionwhich 啟發:JWhich 誕生Unix which cmd 顯示命令實際執行路徑。同理,JWhich 接收類別全名,輸出**類載入器實際載入的 .class 檔絕對路徑**(含 JAR 內部路徑)。
> java JWhich com.myapp.service.UserService
Class 'com/myapp/service/UserService.class' found in
'file:/home/user/projects/myapp/target/classes/com/myapp/service/UserService.class'
> java JWhich javax.servlet.http.HttpServlet
Class 'javax/servlet/http/HttpServlet.class' found in
'file:/home/user/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar!/javax/servlet/http/HttpServlet.class'
public class JWhich {
public static void which(String className) {
// 1. 格式化為資源路徑:/com/myapp/service/UserService.class
if (!className.startsWith("/")) className = "/" + className;
className = className.replace('.', '/') + ".class";
// 2. 請求載入 JWhich 自身的類載入器尋找資源
URL classUrl = JWhich.class.getResource(className);
// 3. 輸出結果
if (classUrl != null) {
System.out.println("Class '" + className + "' found in '" + classUrl.getFile() + "'");
} else {
System.out.println("Class '" + className + "' NOT found in classpath: " + System.getProperty("java.class.path"));
}
}
public static void main(String[] args) {
if (args.length > 0) which(args[0]);
else System.err.println("Usage: java JWhich ");
}
}
Class.getResource() 委派給**載入該類的類載入器**搜尋資源,完美模擬實際類載入邏輯URL 可區分:file:/path/to/file.class(目錄)vs file:/path/to.jar!/pkg/Class.class(JAR 內部)ClassLoader.getResources() 獲取全部匹配 URLexec-maven-plugin / Gradle task 自動掃描衝突類別docker exec app java -cp /app/* JWhich com.foo.Bar 診斷生產環境類衝突| 工具 | 特色 | 適用場景 |
|---|---|---|
java -verbose:class | JVM 參數,啟動時列出所有載入類別來源 | 全域診斷、啟動階段分析 |
arthas / jvm-sandbox | 線上診斷神器,sc -d com.foo.Bar 顯示類載入詳情 | 生產環境零侵入排查 |
maven dependency:tree / gradle dependencyInsight | 構建期依賴衝突分析 | 編譯/打包階段預防 |
| IDE "Open Type" (Ctrl+Shift+T) | 顯示專案與依賴中所有同名類別位置 | 開發期快速定位 |
JWhich 體現了「直接問類載入器」的朴素智慧。雖然現代工具鏈提供更強大的診斷能力,但理解類載入器委派模型、CLASSPATH 搜尋順序、資源路徑格式,仍是每位 Java 開發者解決「類衝突」疑難雜症的內功心法。