Java中如何自定義一個類加載器
如何自定義加載器?
1.創(chuàng)建一個自定義加載器類 繼承 ClassLoader 類
2.重寫 findClass 方法。 主要是實(shí)現(xiàn)從那個路徑讀取 jar包或者.class文件,將讀取到的文件用字節(jié)數(shù)組來存儲,然后可以使用父類的 defineClass 來轉(zhuǎn)換成字節(jié)碼。
如果想破壞雙親委派的話,就重寫 loadClass 方法, 否則不用重寫
注意:
1.ClassLoader提供的 protected final Class<?> defineClass(String name, byte[] b, int off, int len) 是用來將字節(jié)數(shù)組轉(zhuǎn)換成字節(jié)碼文件的,傳入?yún)?shù) 是 (類名,字節(jié)數(shù)組數(shù)據(jù),字節(jié)數(shù)組讀取的開始下標(biāo),字節(jié)數(shù)組的長度)
示例:讀取某文件的下的某class文件
創(chuàng)建一個名為MyClassLoader的類加載器:
import java.io.*; public class MyClassLoader extends ClassLoader{ String path ; MyClassLoader(String dir){ this.path = dir ; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { this.path = this.path + name +".class"; File f = new File(path); InputStream in = new FileInputStream(f); byte [] bys = new byte[ (int)f.length() ]; int len = 0; while( (len = in.read(bys) )!= -1 ){ } // byte[] -> .class return defineClass(name,bys,0,bys.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return super.findClass(name); } }
測試:
在如下目錄,生成一個Hello.class字節(jié)碼文件
Hello.java:
public class Hello { public void sayHello(){ System.out.println("Hello World!"); } }
測試類:
import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { MyClassLoader my = new MyClassLoader("D:\\test\\jvmtest\\"); Class<?> c1 = my.loadClass("Hello"); Object o = c1.newInstance(); Method d = c1.getMethod("sayHello",null); d.invoke(o); } }
運(yùn)行測試類:
類加載器的使用及自定義類加載器
package com.tech.load.def; /** * @author lw * @since 2021/12/3 */ public class UserImpl { static { System.out.println("UserImpl init ..."); } }
package com.tech.load.def; /** * @author lw * @since 2021/12/3 */ public class DefLoader { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { //上下文類加載器,默認(rèn)使用的是 應(yīng)用程序類加載器 // ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // Class<?> c1 = contextClassLoader.loadClass("com.tech.load.def.UserImpl"); // c1.newInstance(); //classloader.loadClass 不會觸發(fā)初始化,當(dāng)創(chuàng)建對象時執(zhí)行初始化,執(zhí)行靜態(tài)程序塊內(nèi)容 輸出 "UserImpl init ..." // ClassLoader contextClassLoader1 = Thread.currentThread().getContextClassLoader(); // Class<?> c2 = contextClassLoader1.loadClass("com.tech.load.def.UserImpl"); // c2.newInstance(); //使用相同的類加載器 加載相同的類名 則加載的是同一個類,c1 c2是同一個類,由于已經(jīng)初始化過 創(chuàng)建對象不再初始化 不再打印 "UserImpl init ..." // System.out.println(contextClassLoader==contextClassLoader1); //true 獲取的上下文類加載器是同一個類加載器 // System.out.println(c1==c2); // true 同一個類加載器器,加載同名的類,第一次加載時加載的類會緩存到類加載器的緩存,再次加載直接在緩存讀取,兩次加載的是同一個類 //直接獲取類的類加載器 應(yīng)用程序類加載器 ClassLoader classLoader = UserImpl.class.getClassLoader(); ClassLoader classLoader1 = UserImpl.class.getClassLoader(); System.out.println(classLoader==classLoader1); //true 獲取的是同一個應(yīng)用程序類加載器 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); System.out.println(classLoader==contextClassLoader); //true 線程上下文類加載器默認(rèn)采用的也是應(yīng)用程序類加載器 與通過類型對象getClassLoader獲取方式相同 Class<?> c1 = Class.forName("com.tech.load.def.UserImpl"); //會觸發(fā)類的初始化 ClassLoader classLoader2 = c1.getClassLoader(); System.out.println(classLoader==classLoader2); //true 獲取的是同一個應(yīng)用程序類加載器 } }
在應(yīng)用程序中,默認(rèn)我們獲取上下文類加載器、類型對象getClassLoader都是采用的同一個應(yīng)用程序類加載器,類在第一次被加載后會緩存到類加載器的緩存中,由于是同一個類加載器此時同名的類不能被多次加載,且應(yīng)用程序類加載器只能加載classpath下的類。
如果我們想加載自定義路徑下的類,需要用到自定義類加載器,可以去指定路徑下加載類,且通過創(chuàng)建多個類加載器對象,加載的同名類相互隔離,也就是說同名類可以被多個自定義類加載器對象加載。
編寫自定義類加載器:
- 繼承ClassLoader;
- 重寫findClass方法在指定路徑下進(jìn)行類的加載,得到字節(jié)數(shù)組,然后使用defineClass根據(jù)字節(jié)數(shù)組生成字節(jié)碼文件 也就是class文件;
編寫一個測試類Goods放在D盤下,javac得到class文件
/** * @author lw * @since 2021/12/3 */ public class Goods { static { System.out.println("Goods init ..."); } }
javac Goods.java
package com.tech.load.def; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; /** * 自定義類加載器 加載類 * @author lw * @since 2021/12/3 */ public class DefLoad7 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader classLoader1 = new MyClassLoader(); Class<?> c1 = classLoader1.loadClass("Goods"); Class<?> c2 = classLoader1.loadClass("Goods"); System.out.println(c1==c2);//true 使用同一個類加載器加載同名類兩次,實(shí)際只加載了一次,第二次是在類加載器的緩存加載的 結(jié)果兩次加載的是同一個 c1.newInstance(); //會初始化 c2.newInstance(); //不會初始化 MyClassLoader classLoader2 = new MyClassLoader(); Class<?> c3 = classLoader2.loadClass("Goods"); System.out.println(c1==c3); //false 使用不同的類加載器對同一個類進(jìn)行加載,會得到不同的類型對象 c3.newInstance(); //會初始化 } } //自定義類加載器 加載D盤下的類 class MyClassLoader extends ClassLoader{ //去指定的路徑下加載類 //name是類名稱 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String path="D:\\"+name+".class"; try { ByteArrayOutputStream os = new ByteArrayOutputStream(); //將指定路徑下的文件 拷貝到輸出流 Files.copy(Paths.get(path),os); byte[] bytes = os.toByteArray(); //調(diào)用父類的方法 根據(jù)字節(jié)數(shù)組生成字節(jié)碼文件 也就是class文件 //bytes -> *.class return defineClass(name,bytes,0,bytes.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException("類文件未找到",e); } } }
使用自定義加載器,創(chuàng)建多個類加載器對象去加載同一個類,會得到多個類型對象。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Bean實(shí)例化實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring Bean實(shí)例化實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02java實(shí)現(xiàn)建造者模式(Builder Pattern)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)建造者模式Builder Pattern,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10java實(shí)現(xiàn)遍歷樹形菜單兩種實(shí)現(xiàn)代碼分享
這篇文章主要介紹了java實(shí)現(xiàn)遍歷樹形菜單兩種實(shí)現(xiàn)代碼分享,兩種實(shí)現(xiàn):OpenSessionView實(shí)現(xiàn)、TreeAction實(shí)現(xiàn)。具有一定參考價值,需要的朋友可以了解下。2017-11-11