java自定義類加載器如何實現(xiàn)類隔離
網(wǎng)上java自定義類加載器很多容易找到,但是都是加載的單個類,如果被加載的類,有引用了其他類怎么辦呢?接下來看一下如何來處理這種情況
有時候一個項目中可能會引用不同版本的第三方依賴,比如筆者在升級hbase系統(tǒng)時,代理層就同時用到了1.X和2.X版本的hbase-client的jar包。
當時是使用的阿里的SOFAArk來實現(xiàn)的。
它的本質(zhì)就是是喲個類加載來實現(xiàn)的,接下來就通過一個小例子來通過自定義類加載器實現(xiàn)類隔離。
下面實例實現(xiàn)了在同一個應(yīng)用里加載了兩個類名報名完全相同的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); //通過反射調(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); //通過反射調(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
通過繼承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(); //依賴的類需要提前加載,不是應(yī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); //通過反射調(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); //通過反射調(diào)用Test類的say方法 method1.invoke(obj1, null); } catch (Exception e) { e.printStackTrace(); } } }
運行結(jié)果:
a hi ...
a hello tom
b hi ...
b hello cat
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
自定義Jackson的ObjectMapper如何實現(xiàn)@ResponseBody的自定義渲染
這篇文章主要介紹了自定義Jackson的ObjectMapper如何實現(xiàn)@ResponseBody的自定義渲染,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07SpringBoot項目如何把接口參數(shù)中的空白值替換為null值(推薦)
這篇文章主要介紹了SpringBoot項目如何把接口參數(shù)中的空白值替換為null值(推薦),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01springboot 傳參校驗@Valid及對其的異常捕獲方式
這篇文章主要介紹了springboot 傳參校驗@Valid及對其的異常捕獲方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10在SpringBoot中使用jwt實現(xiàn)token身份認證的實例代碼
你還不會在SpringBoot中使用jwt實現(xiàn)token身份認證嗎,本文小編就給大家詳細的介紹一下在SpringBoot中使用jwt實現(xiàn)token身份認證的實例代碼,感興趣的同學可以自己動手試一試2023-09-09