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

自定義類加載器
準(zhǔn)備
準(zhǔn)備工作,在D盤的a,b兩個(gè)目錄下準(zhǔn)備Hello.class 和 Dog.class 兩個(gè)文件,可以使用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());
}
}
通過(guò)URLClassLoader來(lái)實(shí)現(xiàn)【推薦】
類加載器會(huì)主動(dòng)加載被引用的類。比如類A依賴引用了類B ,則只需要主動(dòng)加載類A即可,類B會(huì)自動(dòng)加載,而且加載類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,類加載器會(huì)主動(dòng)加載被引用的類。
* 注意一般是我們使用 URLClassLoader 實(shí)現(xiàn)自定義的類加載器。如果使用classLoader,則需要重寫findClass方法來(lái)實(shí)現(xiàn)類字節(jié)碼的加載
*/
Method method = clz.getMethod("sayHello", null);
//通過(guò)反射調(diào)用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);
//通過(guò)反射調(diào)用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
通過(guò)繼承ClassLoader實(shí)現(xiàn)
網(wǎng)上說(shuō)可以自定義類加載器實(shí)現(xiàn),接下來(lái)我們看一下(jdk11直接忽略,因?yàn)榫幾g不通過(guò),下面是在jdk8版本測(cè)試)。
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;
}
}編寫測(cè)試類
package 自定義類加載器;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ClassLoaderTest {
/**
* 集成ClassLoader,重寫findClass實(shí)現(xiàn)自定義類加載器
*/
@Test
public void test1() {
try {
MyClassLoader diskLoader = new MyClassLoader();
MyClassLoader diskLoader1 = new MyClassLoader();
//依賴的類需要提前加載,不是應(yīng)該自動(dòng)加載的嗎?
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);
//通過(guò)反射調(diào)用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);
//通過(guò)反射調(diào)用Test類的say方法
method1.invoke(obj1, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}運(yùn)行結(jié)果:
a hi ...
a hello tom
b hi ...
b hello cat
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
自定義Jackson的ObjectMapper如何實(shí)現(xiàn)@ResponseBody的自定義渲染
這篇文章主要介紹了自定義Jackson的ObjectMapper如何實(shí)現(xiàn)@ResponseBody的自定義渲染,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Jdk1.8 HashMap實(shí)現(xiàn)原理詳細(xì)介紹
這篇文章主要介紹了Jdk1.8 HashMap實(shí)現(xiàn)原理詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12
shardingJdbc3.x?版本的分頁(yè)bug問(wèn)題解析
這篇文章主要為大家介紹了shardingJdbc3.x?版本的分頁(yè)問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
SpringBoot項(xiàng)目如何把接口參數(shù)中的空白值替換為null值(推薦)
這篇文章主要介紹了SpringBoot項(xiàng)目如何把接口參數(shù)中的空白值替換為null值(推薦),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
springboot 傳參校驗(yàn)@Valid及對(duì)其的異常捕獲方式
這篇文章主要介紹了springboot 傳參校驗(yàn)@Valid及對(duì)其的異常捕獲方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼
你還不會(huì)在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證嗎,本文小編就給大家詳細(xì)的介紹一下在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼,感興趣的同學(xué)可以自己動(dòng)手試一試2023-09-09

