類加載器採用雙親委托模式:子加載器先委托父加載器,父加載器找不到時回退到子加載器。作者編寫自定義類加載器測試正常,但在 Ant <java> 任務中表現異常:自定義加載器未委托父加載器,直接自行加載。
f:\project
├── src\cn\itcast\MainClass.java, AuxiliaryClass.java
├── classes\cn\itcast\MainClass.class, AuxiliaryClass.class
└── build.xml
<target name="run">
<java classname="cn.itcast.MainClass">
<classpath><pathelement location="${classes.dir}"/></classpath>
</java>
</target>
輸出:
org.apache.tools.ant.loader.AntClassLoader2
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
cn.itcast.AuxiliaryClass
正常委托鏈:AntClassLoader2 → AppClassLoader → ExtClassLoader。
set CLASSPATH=f:\project\classes;
ant run
輸出:
java.net.URLClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
cn.itcast.AuxiliaryClass
結論:CLASSPATH 環境讓 Ant 使用 URLClassLoader 而非 AntClassLoader2。
f:\project\cn\itcast\AuxiliaryClass.class (獨立目錄)
f:\project\classes\cn\itcast\MainClass.class
設置 CLASSPATH=f:\project\classes;f:\project; (含獨立目錄)。
public class MainClass extends ClassLoader {
private String path;
public MainClass(String path) { this.path = path; }
protected Class findClass(String name) throws ClassNotFoundException {
// 從 path 目錄讀取 .class 字節碼 → defineClass
}
public static void main(String[] args) throws Exception {
Class cls = new MainClass(args[1]).loadClass(args[0]);
ClassLoader loader = cls.getClassLoader();
while (loader != null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
}
}
ant -Darg0=cn.itcast.AuxiliaryClass -Darg1=cn\itcast
輸出:
cn.itcast.MainClass
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
異常點:第一行顯示 AuxiliaryClass 的加載器是 MainClass (自定義加載器)。按委托機制,MainClass 應先委托父加載器 AppClassLoader,後者能從 CLASSPATH 找到 AuxiliaryClass,故應由 AppClassLoader 加載。
java cn.itcast.MainClass cn.itcast.AuxiliaryClass cn\itcast
輸出:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
正常:AppClassLoader 加載 AuxiliaryClass,委托機制生效。
set CLASSPATH=f:\project\classes;
cd f:\
java cn.itcast.MainClass cn.itcast.AuxiliaryClass project\cn\itcast
輸出:
cn.itcast.MainClass
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
此時 AppClassLoader 找不到類,委托回退到 MainClass 成功加載,符合預期。
為什麼在 Ant 環境下,自定義類加載器繞過父加載器 (AppClassLoader) 直接自行加載? 而在 java 命令下委托機制正常工作。
loadClass(),改變委托邏輯org.apache.tools.ant.taskdefs.ExecuteJava 如何創建子 JVM/類加載器ClassLoader.getSystemClassLoader() 返回何者?loadClass() 中打印 getParent()、getSystemClassLoader()、Thread.currentThread().getContextClassLoader()java 命令與 Ant 任務下的上述三者輸出AntClassLoader2、ExecuteJava 源碼 (Ant 源碼包中)<java fork="true"> 強制啟動新 JVM,觀察是否恢復正常此問題揭示了構建工具類加載器隔離機制對運行時委托模式的潛在影響,值得深入研究 Ant 源碼機制。