java自定義類加載器如何實現(xiàn)類隔離
網(wǎng)上java自定義類加載器很多容易找到,但是都是加載的單個類,如果被加載的類,有引用了其他類怎么辦呢?接下來看一下如何來處理這種情況
有時候一個項目中可能會引用不同版本的第三方依賴,比如筆者在升級hbase系統(tǒng)時,代理層就同時用到了1.X和2.X版本的hbase-client的jar包。
當時是使用的阿里的SOFAArk來實現(xiàn)的。
它的本質就是是喲個類加載來實現(xiàn)的,接下來就通過一個小例子來通過自定義類加載器實現(xiàn)類隔離。
下面實例實現(xiàn)了在同一個應用里加載了兩個類名報名完全相同的Hello 和Dog類:

自定義類加載器
準備
準備工作,在D盤的a,b兩個目錄下準備Hello.class 和 Dog.class 兩個文件,可以使用javac編譯。
//a文件夾:
public class Hello {
private String name;
public Hello(String name) {
this.name = name;
}
public void sayHello() {
Dog d =new Dog();
d.hi();
System.out.println("a hello " + name + " "+ this.getClass().getClassLoader());
}
}
public class Dog {
public void hi() {
System.out.println("a hi ... "+ this.getClass().getClassLoader());
}
}
//b文件夾
public class Hello {
private String name;
public Hello(String name) {
this.name = name;
}
public void sayHello() {
Dog d =new Dog();
d.hi();
System.out.println("b hello " + name + " "+ this.getClass().getClassLoader());
}
}
public class Dog {
public void hi() {
System.out.println("b hi ... "+ this.getClass().getClassLoader());
}
}
通過URLClassLoader來實現(xiàn)【推薦】
類加載器會主動加載被引用的類。比如類A依賴引用了類B ,則只需要主動加載類A即可,類B會自動加載,而且加載類B的類加載器與類A相同。
/**
* URLClassLoader
* 推薦此方法
*/
@Test
public void test0() {
try {
System.out.println( this.getClass().getClassLoader());
URLClassLoader diskLoader = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/a/")});//最后面的斜杠需要添加
URLClassLoader diskLoader1 = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/b/")});
//加載class文件
Class clz = diskLoader.loadClass("Hello");
Constructor constructor = clz.getConstructor(String.class);
Object obj = constructor.newInstance("tom");
/**
* 類Hello引用了類Dog,類加載器會主動加載被引用的類。
* 注意一般是我們使用 URLClassLoader 實現(xiàn)自定義的類加載器。如果使用classLoader,則需要重寫findClass方法來實現(xiàn)類字節(jié)碼的加載
*/
Method method = clz.getMethod("sayHello", null);
//通過反射調用Test類的say方法
method.invoke(obj, null);
Class clz1 = diskLoader1.loadClass("Hello");
Constructor constructor1 = clz1.getConstructor(String.class);
Object obj1 = constructor1.newInstance("cat");
Method method1 = clz1.getMethod("sayHello", null);
//通過反射調用Test類的say方法
method1.invoke(obj1, null);
} catch (Exception e) {
e.printStackTrace();
}
}輸出
jdk.internal.loader.ClassLoaders$AppClassLoader@1f89ab83
a hi ... java.net.URLClassLoader@3891771e
a hello tom java.net.URLClassLoader@3891771e
b hi ... java.net.URLClassLoader@78ac1102
b hello cat java.net.URLClassLoader@78ac1102
通過繼承ClassLoader實現(xiàn)
網(wǎng)上說可以自定義類加載器實現(xiàn),接下來我們看一下(jdk11直接忽略,因為編譯不通過,下面是在jdk8版本測試)。
package 自定義類加載器;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
/**
* 自定義ClassLoader
*/
public class MyClassLoader extends ClassLoader {
protected Class<?> findClass(String path, String name) {
try {
FileInputStream in = new FileInputStream(path + name + ".class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1) {
baos.write(buf, 0, len);
}
in.close();
byte[] classBytes = baos.toByteArray();
return defineClass(name, classBytes, 0, classBytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}編寫測試類
package 自定義類加載器;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ClassLoaderTest {
/**
* 集成ClassLoader,重寫findClass實現(xiàn)自定義類加載器
*/
@Test
public void test1() {
try {
MyClassLoader diskLoader = new MyClassLoader();
MyClassLoader diskLoader1 = new MyClassLoader();
//依賴的類需要提前加載,不是應該自動加載的嗎?
diskLoader.findClass("D:\\liubenlong\\a\\", "Dog");
diskLoader1.findClass("D:\\liubenlong\\b\\", "Dog");
//加載class文件
Class clz = diskLoader.findClass("D:\\liubenlong\\a\\", "Hello");
Constructor constructor = clz.getConstructor(String.class);
Object obj = constructor.newInstance("tom");
Method method = clz.getMethod("sayHello", null);
//通過反射調用Test類的say方法
method.invoke(obj, null);
Class clz1 = diskLoader1.findClass("D:\\liubenlong\\b\\", "Hello");
Constructor constructor1 = clz1.getConstructor(String.class);
Object obj1 = constructor1.newInstance("cat");
Method method1 = clz1.getMethod("sayHello", null);
//通過反射調用Test類的say方法
method1.invoke(obj1, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}運行結果:
a hi ...
a hello tom
b hi ...
b hello cat
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
java天數(shù)計算函數(shù)(當前月天數(shù)、某月總天數(shù)及某月剩余天數(shù))4種方法實現(xiàn)代碼
日常開發(fā)中會遇到關于日期的計算,比如當月的天數(shù)、兩日期之間的天數(shù)、當月剩余天數(shù)等等,這篇文章主要給大家介紹了關于java天數(shù)計算函數(shù)(當前月天數(shù)、某月總天數(shù)及某月剩余天數(shù))4種方法實現(xiàn)的相關資料,需要的朋友可以參考下2023-10-10
使用JSONObject.toJSONString 過濾掉值為空的key
這篇文章主要介紹了使用JSONObject.toJSONString 過濾掉值為空的key,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
淺談Java關閉線程池shutdown和shutdownNow的區(qū)別
本文主要介紹了Java關閉線程池shutdown和shutdownNow的區(qū)別,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09

