類載入器ClassLoader之jar包隔離
阿新 • • 發佈:2018-12-06
小引子
最近做了一個根據同一模組的不同jar版本做同時測試的工具,感覺挺有意思,特此記錄。
類載入器(ClassLoader)是啥?
把類載入階段中的“通過一個類的全限定名(博主注:絕對路徑)來獲取描述此類的二進位制位元組流”這個動作放在Java虛擬機器外部去實現,以便讓應用程式自己決定如何去獲取所需要的類。實現這個動作的程式碼模組成為”類載入器“。摘自周志明的《深入理解Java虛擬機器》
ClassLoader的用途
- 功能測試
每個載入器,有自己的獨立的類名稱空間。比較兩個類是否”相等“的前提是它們是由同一個類載入載入才有意義,即ClassLoader如果不同,兩個類必定不等。這樣使得在一個JVM中載入同一個模組的不同版本的jar成為現實,基於反射功能,我們同樣可以很輕鬆實現不同版本的模組測試。本文後面會提供簡單demo的實現。 - 程式碼加密
沒有做過,想必是對class檔案進行混淆、壓縮、native等等手段後的解密過程,這類需求還沒遇過。 - OSGi
是動態模型形同,在eclipse中外掛的實現就是基於OSGi思想,而eclipse主要的應用就是外掛,所以可以理解為eclipse外掛是OSGi的應用典範。做的不多,僅限於瞭解。 - 熱部署
不停止服務,動態替換目標檔案。ClassLoader動態載入jar包,如果做一個工程化的東西可能會費些周章,但是原理並不複雜。 - ...
總之,ClassLoader很重要,Java世界需要它。
功能測試小樣
本人在本地生成了test1.jar和test2.jar兩個jar包。這兩個jar都有類com.array7.jvm.classloader.Target
** test1.jar Target.java **
package com.array7.jvm.classloader;
public class Target {
public static void main(String[] args) {
System.out.print("test1");
}
}
** test2.jar Target.java **
package com.array7.jvm.classloader; public class Target { public static void main(String[] args) { System.out.print("test2"); } }
** TestDriver**
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
ClassLoader loader1 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test1/test1.jar")}, TestDriver.class.getClassLoader());
ClassLoader loader2 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test2/test2.jar")}, TestDriver.class.getClassLoader());
String className = "com.array7.jvm.classloader.Target";
// loader1
System.out.print("test1.jar \t");
Class clazz1 = Class.forName(className, true, loader1);
clazz1.getMethod("main", String[].class).invoke(null, (Object) null);
System.out.println();
// loader2
System.out.print("test2.jar \t");
Class clazz2 = Class.forName(className, true, loader2);
clazz2.getMethod("main", String[].class).invoke(null, (Object) null);
System.out.println();
System.out.println("例項化後是否相等:" + clazz1.equals(clazz2));
}
輸出
test1.jar test1
test2.jar test2
例項化後是否相等:false
其他未提知識點
- ClassLoader的層級關係
- 雙親委託與打破
- 自定義ClassLoader
其他參考資料
分類: Java
/**
*
*/
package com.codemacro.jcm.main;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author HUANGLIAO322
* @date 2018年9月10日
*
*/
public class TestDriver {
public static void main(String[] args) throws Exception {
// ClassLoader loader1 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test1/test1.jar")}, TestDriver.class.getClassLoader());
// ClassLoader loader2 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test2/test2.jar")}, TestDriver.class.getClassLoader());
URL[] url1 = new URL[]{new URL("file:D:/Users/HUANGLIAO322/Desktop/bundle/jcm/lib/jcm.jar")};
URL[] url2 = new URL[]{new URL("file:D:/Users/HUANGLIAO322/Desktop/bundle/jcm/lib/jcm1.jar")};
ClassLoader loader1 = new URLClassLoader(url1, TestDriver.class.getClassLoader());
ClassLoader loader2 = new URLClassLoader(url2, TestDriver.class.getClassLoader());
String className = "com.codemacro.jcm.JCMMain";
// loader1
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^");
System.out.println("jcm.jar \t");
Class clazz1 = Class.forName(className, true, loader1);
clazz1.getMethod("main", String[].class).invoke(null, (Object) null);
System.out.println(clazz1.getClassLoader());
System.out.println();
// loader2
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^");
System.out.println("jcm1.jar \t");
Class clazz2 = Class.forName(className, true, loader2);
clazz2.getMethod("main", String[].class).invoke(null, (Object) null);
System.out.println(clazz2.getClassLoader());
System.out.println("例項化後是否相等:" + clazz1.equals(clazz2));
System.out.println(clazz1.getClassLoader().getParent().equals(TestDriver.class.getClassLoader()));
}
}