类加载器源码、双亲委派、自定义类加载器详解
文章目錄
- jdk的類加載器
- 雙親委派
- 自定義類加載器
- 打破雙親委派
jdk的類加載器
查看一個類的類加載器:
ClassLoader classLoader = boy.class.getClassLoader();查看父加載器:
ClassLoader classLoader = boy.class.getClassLoader().getParent();如果父加載器是根加載器,打印的是null
例如
查看classLoader 的loadClass源碼:
public abstract class ClassLoader { //加載具有指定二進制名稱的類。此方法的默認實現(xiàn)按以下順序搜索類:調(diào)用 findLoadedClass(String) 以檢查該類是否已加載。在父類加載器上調(diào)用 loadClass 方法。如果 parent 為 null,則使用虛擬機內(nèi)置的類加載器。調(diào)用 findClass(String) 方法來查找 class private final ClassLoader parent;//parent不是繼承關系,而是作為成員變量protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {//首先檢查是否已經(jīng)加載過Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found//從非空父類加載器}if (c == null) {// 如果仍未找到,則調(diào)用 findClass 以查找該類long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}loadClass的類加載過程有如下幾步:
加載 >> 驗證 >> 準備 >> 解析 >> 初始化 >> 使用 >> 卸載
加載:在硬盤上查找并通過IO讀入字節(jié)碼文件,使用到類時才會加載,例如調(diào)用類的main()方法,new對象等等,在加載階段會在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口
加載完成后,Class 對象還不完整,所以此時的類還不可用。當類被加載后就進入連接階段,這一階段包括驗證(校驗字節(jié)碼文件)、準備(為靜態(tài)變量分配內(nèi)存并設置默認的初始值)和解析(將符號引用替換為直接引用)三個步驟。最后JVM 對類進行初始化,包括:
1 如果類存在直接的父類并且這個類還沒有被初始化,那么就先初始化父類;
2 如果類中存在初始化語句,就依次執(zhí)行這些初始化語句。
類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統(tǒng)加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類)。從JDK 1.2 開始,類加載過程采取了父親委托機制(PDM)。PDM 更好的保證了Java 平臺的安全性,在該機制中,JVM 自帶的Bootstrap 是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。JVM 不會向Java 程序提供對Bootstrap 的引用。
注意,主類在運行過程中如果使用到其它類,會逐步加載這些類。
jar包或war包里的類不是一次性全部加載的,是使用到時才加載。
雙親委派
當“應用程序類加載器"接到一個加載任務時
(1)先搜素內(nèi)存中是否已經(jīng)加載過了,如果加載 過了,就可以找到對應的C1ass對象,那么就不加載了
(2)如 果沒有找到,把這個任務先提交給 parent",父加載器接到任務時,也是重復(1)(2)
3)直到傳給了根加載器,如果根加載器可以加載,就完成了,如果不能加戟,往回傳,依次每 個加載器嘗試在自己負責的路徑下搜素,如果找到就直接返回Class對象,如果一直回傳到“應用程序類加載器”,還是沒有找到就會報ClassnotFoundException
為什么用這種機制:
為了安全,防止你寫一個核心類。
每一個加載器只負責自己路徑下的東西。
類加載器的作用
1、最主要的作用 :加載類
2、輔助的作用:可以用它 來加載“類路徑下”的資源文件
例如:bin中src下文件ー->bin目錄下Properties.Propertie s類表示了一個持久的屬性集, Properties可保存在流中或從中加載屬性列表中每個鍵
舉個例子,ClassLoader 加載的class 文件來源很多,比如編譯器編譯生成的class、或者網(wǎng)絡下載的字節(jié)碼。
而一些來源的class 文件是不可靠的,比如我可以自定義一個java.lang.Integer 類來覆蓋jdk 中默認的Integer
例如下面這樣:
package java.lang;public class Integer {public Integer(int value) { System.exit(0); } }初始化這個Integer 的構(gòu)造器是會退出JVM,破壞應用程序的正常進行,如果使用雙親委派機制的話該Integer 類永遠不會被調(diào)用,以為委托BootStrapClassLoader 加載后會加載JDK 中的Integer 類而不會加載自定義的這個
自定義類加載器
public class MyClassLoader extends ClassLoader{private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass將一個字節(jié)數(shù)組轉(zhuǎn)為Class對象,這個字節(jié)數(shù)組是class文件讀取后最終的字節(jié)數(shù)組。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載器設置為應用程序類加載器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盤創(chuàng)建 test/com/test/jvm 幾級目錄,將User類的復制類User1.class丟入該目錄Class clazz = classLoader.loadClass("com.test.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());} }打破雙親委派
public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}/*** 重寫類加載方法,實現(xiàn)自己的加載邏輯,不委派給雙親加載* @param name* @param resolve* @return* @throws ClassNotFoundException*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}}public static void main(String args[]) throws Exception {MyClassLoader classLoader = new MyClassLoader("D:/test");//嘗試用自己改寫類加載機制去加載自己寫的java.lang.String.classClass clazz = classLoader.loadClass("java.lang.String");Object obj = clazz.newInstance();Method method= clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());} }運行結(jié)果: java.lang.SecurityException: Prohibited package name: java.langat java.lang.ClassLoader.preDefineClass(ClassLoader.java:659)at java.lang.ClassLoader.defineClass(ClassLoader.java:758)總結(jié)
以上是生活随笔為你收集整理的类加载器源码、双亲委派、自定义类加载器详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微服务秒杀项目整合网关+feign+re
- 下一篇: class字节码文件中的常量池结构详解