java~通過ClassLoader動態加載~tomcat模型
在進行非WEB項目(Springboot)進行開發時,使用classLoader進行動態加載jar,并使用接口進行強類型轉換是沒有問題的,它們使用JVM下的URLClassLoader進行實現,而在基于tomcat的容器里使用它時,出現了類型無法找到的問題,原因如下:
tomcat有個叫webApp的加載器它是先加載WEB-INF/classes后在加載WEB-INF/lib,但它的父加載器是它的common加載器,comon的父加載器是system加載器(和JVM的應用程序加載器功能差不多,不過指定了其他tomcat目錄下的加載,大家可以看看官網上的英文文檔),但是源碼中這個加載器是URLClassLoader的子類,而URLClassLoader默認父加載tomcat下是它的system加載器這么設計和tomcat的
配置有關,默認為無為false,會直接委托給tomcat的system加載器加載system委托最頂層的Bootstrap加載器(差不多是JVM里起始加載器和擴展加載器的合并),但不管怎么樣,項目在tomcat下自定義的或者URLClassLoader加載默認父加載器都不會是tomcat的webApp加載器而是system加載器,或者自定義的加載器或URLClassLoader和tomcat的webApp加載器沒有上下關系,所以動態創建類時設計到其他類時肯定會報CNF異常。
參考
https://blog.csdn.net/chenleixing/article/details/50042795
https://blog.csdn.net/chenleixing/article/details/47121661
http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
Tomcat ClassLoader

解決方法
一 解決思路就是先獲取當前類的Class,然后獲取當前類的加載器,在自定義的加載器或者URLClassLoader加載器創建時指定為它們的父加載器,這樣問題就會游刃而解了,可能平常我們測試寫個簡單的例子沒遇到這個問題,因為我們那時的URLClassLoader或者自定義的加載器的父加載器都是JVM的第三次加載器即應用程序加載,它是專門加載classpath下邊的或者指定的類或者jar的,依照雙親委托模型,肯定會找到引入路徑的那個類或者jar的。
二 使用Class.forName()的方式來動態加載指定的類,就不會存在這個問題,因為這種方式一方面是能初始化類的靜態東西,再就是重要一點,就是采用的加載當前所在類的加載器來加載你指定的類,這樣你在tomcat下那就是它的webApp加載器啊,肯定不再出現這個問題,可能直接就從緩存里找到了。
動態加載代碼
URL url = new URL(packageUrl);
// IDEA調試時與java運行時的ClassLoader是不同的,所以需要使用當前環境下的ClassLoader
ClassLoader loader = new URLClassLoader(new URL[]{url}, clazz.getClassLoader()) {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException(name);
}
}
};
浙公網安備 33010602011771號