Java類加載機制及自定義加載器
一:ClassLoader類加載器,主要的作用是將class文件加載到jvm虛擬機中。jvm啟動的時候,並不是一次性加載所有的類,而是根據需要動態去加載類,主要分為隱式加載和顯示加載。
隱式加載:程序代碼中不通過調用ClassLoader來加載需要的類,而是通過JVM類自動加載需要的類到內存中。例如,當我們在類中繼承或者引用某個類的時候,JVM在解析當前這個類的時,發現引用的類不在內存中,那麽就會自動將這些類加載到內存中。
顯示加載:代碼中通過Class.forName(),this.getClass.getClassLoader.LoadClass(),自定義類加載器中的findClass()方法等。
Thread. setContextClassLoader() 上下文加載器
是一個角色 用以解決頂層ClassL
類加載器的繼承關系
類加載器之間的繼承關系,如下圖:
ExtClassLoader,AppClassLoder繼承URLClassLoader,而URLClassLoader繼承ClassLoader,BoopStrap ClassLoder不在上圖中,因為它是由C/C++編寫的,它本身是虛擬機的一部分,並不是一個java類。jvm加載的順序:BoopStrap ClassLoder-〉ExtClassLoader->AppClassLoder
oader無法訪問底層ClassLoader的類的問題 基本思想是,在頂層ClassLoader中,傳入底層ClassLoader的實例
AppClassLoader的父加載器為ExtClassLoader,ExtClassLoader的父加載器為null,BoopStrap ClassLoader為頂級加載器。
一個普通的加載過程:
(1)首先會到自定義加載器中查找,看是否已經加載過,如果已經加載過,則返回字節碼。
(2)如果自定義加載器沒有加載過,則詢問上一層加載器(即AppClassLoader)是否已經加載過Test.class。
(3)如果沒有加載過,則詢問上一層加載器(ExtClassLoader)是否已經加載過。
(4)如果沒有加載過,則繼續詢問上一層加載(BoopStrap ClassLoader)是否已經加載過。
(5)如果BoopStrap ClassLoader依然沒有加載過,則到自己指定類加載路徑下("sun.boot.class.path")查看是否有Test.class字節碼,有則返回,沒有通
知下一層加載器ExtClassLoader到自己指定的類加載路徑下(java.ext.dirs)查看。
(6)依次類推,最後到自定義類加載器指定的路徑還沒有找到Test.class字節碼,則拋出異常ClassNotFoundException。如下圖:
藍線:findLoadedClass
紅線:loadClass
類加載過程的幾個方法
(1)loadClass (2)findLoadedClass (3)findClass
當父類加載器沒有加載,並且沒有相關的類,則當前類的加載器調用自身findClass,去loadClass
自定義類加載器步驟
(1)繼承ClassLoader (2)重寫findClass()方法 (3)調用defineClass()方法
下面寫一個自定義類加載器:指定類加載路徑在D盤下的lib文件夾下。
(1)新建一個Test.class類,代碼如下:
package test; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class MyClassLoader extends ClassLoader{ private String classpath; public MyClassLoader(String classpath) { this.classpath = classpath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte [] classDate=getDate(name); if(classDate==null){} else{ //defineClass方法將字節碼轉化為類 return defineClass(name,classDate,0,classDate.length); } } catch (IOException e) { e.printStackTrace(); } return super.findClass(name); } //返回類的字節碼 private byte[] getDate(String className) throws IOException{ InputStream in = null; ByteArrayOutputStream out = null; String path=classpath + File.separatorChar + className.replace(‘.‘,File.separatorChar)+".class"; try { in=new FileInputStream(path); out=new ByteArrayOutputStream(); byte[] buffer=new byte[2048]; int len=0; while((len=in.read(buffer))!=-1){ out.write(buffer,0,len); } return out.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } finally{ in.close(); out.close(); } return null; } }
package test; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestMyClassLoader { public static void main(String []args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{ //自定義類加載器的加載路徑 MyClassLoader myClassLoader=new MyClassLoader("D:\\lib"); //包名+類名 Class c=myClassLoader.loadClass("com.test.Test"); if(c!=null){ Object obj=c.newInstance(); Method method=c.getMethod("say", null); method.invoke(obj, null); System.out.println(c.getClassLoader().toString()); } } }
自定義類加載器的作用:jvm自帶的三個加載器只能加載指定路徑下的類字節碼。如果某個情況下,我們需要加載應用程序之外的類文件呢?比如本地D盤下的,或者去加載網絡上的某個類文件,這種情況就可以使用自定義加載器了。
JDK中ClassLoader默認設計模式 – 問題:
Thread. setContextClassLoader() 上下文加載器 是一個角色 ,用以解決頂層ClassLoader無法訪問底層ClassLoader的類的問題
基本思想是,在頂層ClassLoader中,傳入底層ClassLoader的實例
雙親模式的破壞:
雙親模式是默認的模式,但不是必須這麽做
Tomcat的WebappClassLoader 就會先加載自己的Class,找不到再委托parent
OSGi的ClassLoader形成網狀結構,根據需要自由加載Class
熱替換:
當一個class被替換後,系統無需重啟,替換的類立即生效
Java類加載機制及自定義加載器