深入分析classloader工作機制_第1頁
深入分析classloader工作機制_第2頁
深入分析classloader工作機制_第3頁
深入分析classloader工作機制_第4頁
深入分析classloader工作機制_第5頁
已閱讀5頁,還剩12頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

ClassLoaderClassLoaderClassJVM賓發(fā)放入會證明,入會的嘉賓分為VIP會員、黃金會員、白金會員和普通會員等。對應的接待室也會分VIP會員接待室、黃金會員接待室白金會員接待室和普通接待室,不同等級的會員會被不同的接待室接待。所有的會員要想進入會場只有位大佬但是你不是VIP接待室接待的,那么對不起你仍然不是VIP會員。當然你是不是VIP會員,會有嚴格的審查規(guī)ClassLoaderClassJVM中之外,還有一個重要的作用就是,審查每個類應該由誰加載,他是一種ClassLoader除了上述兩個作用外還有一個任務就是將Class字節(jié)碼重新解析成JVM統(tǒng)一要求的對象格式。這個格式是什么將會在后面的Class文件格式分析一章本章將主要分析CLassLoader的前兩個作用:也就是ClassLoader的加載機制和如何加載類的過程。另外還將著重介紹JavaWeb中常用的ClassLoader是如何實現的,理解他們將幫助我們在日常的開發(fā)過程中更加理解我們的程序是如果工作ClassLoader如下圖所示,我們經常用到或擴展 ClassLoader其中defineClass方法是用來將byte字節(jié)流解析成JVM能夠識別的Class對象,有了這個方法意味著我們不僅僅可以通過class文件實例化對象,還可以通過其他方式如我們通過網絡接收到一個類的字節(jié)碼,拿這個字節(jié)碼流直接創(chuàng)建類的Class對象形式。注意如果直接調用這個方法生成的類的Class對象,這個類的Class對象還沒有resolve,這個resolvedefineClass通常是和findClass方法一起使用的,我們通過會直接覆蓋ClassLoader父類的findClass方法,實現我們自己的類的加載規(guī)則,然后取得我們要加載類的字節(jié)碼,然后調用defineClass方法生成類的Class對象,如果你想在類被加載到JVM中時就希望這個類被LinkresolveClassJVM來link這個類。如果你不想重新定義加載類的規(guī)則,也沒有復雜的處理邏輯,只想在運行時能夠加載自己制定的一個類而已,那么你可以這樣thi.getClas().getlassLoader().loadlas("clas")調用lasLoader的loadlass方法就可以獲取這個類的lass對象,這個loadClas還有重載方法你同樣可以決定在什么時候reolve這個clas。ClassLoader是個抽象類,他還有很多子類,我們如果要實現自己的ClassLoader的話,一般都會繼承ServletHttpServlet前面介紹的這幾個方法都是我們要擴展ClassLoader時需要打交道的,ClassLoader還提供了另外一些輔助方法,如獲取class文件的方法如getResource以及getResourceAsStream等。還有就是獲取SystemClassLoader的方法等,關于這些方ClassLoader在前面的會員進入會場的規(guī)則中,如何保證不同等級的會員通過不同的會員接待室接待進入會場?因為可能存在一些會員可能并不能自己就能正確的找到接待自己的接待室的情況,也有可能有些會員會冒充更高級的會員身份混進去這種情況,所以必須要有機制能夠保證所有會員都被正確的接待室接待進入會場,而且一個會員只能被一個接待室接待,不能出現被兩個接待室重復接待的情況出現,也就是不能同時拿到兩個入場證明,也就是要保證接待的一致性。如何設計這個接待規(guī)則呢?lassLoader就設計了這樣一致接待機制,這個機制就是上級委托接待機制。他是這樣的:任何一個會員到達任何一個會員接待室時,這個接待室首先會檢查這個會員是否已經被自己接待過,如果已經接待過的話,則拒絕本次接待也就是不再發(fā)入會證明了,如果沒有接待過的話,那么會向上詢問,這個會員是否應該在上一級的更高級別的接待室接待,上級接待室會根據他們的接待規(guī)則,檢查這個會員是否被已經接到過,如果已經接待過同樣的處理方法,將已經接待的結果反饋給下一級,如果也沒有接待過的話,他再向更高一級(如果有更高一級的話)接到室轉發(fā)接待請求,更高一級也是同樣的處理方法,知道有一級接待接待或者告訴他下一級,這個會員不是自己接待這個結果。如果這個會員來到的這個接待室得到他上一級的接待室反饋這個會員沒有被接待,并且也不應該是由他們接待的話,這個接待室將會正式接待這個會員,并發(fā)給他入會證明,那么這個會員就被定義為這個接待室等級的會員身份。這種接待規(guī)則看上去有點麻煩,但是他卻能夠保證所有的會員能夠被正確的接待室接待,會員的身份也不會錯誤,也不存在冒充身份的會員。JVMClassLoaderClassLoader可以分為兩種類型,可以理解為為接待室服務的接待室和BoottraplasLoader,這個lasLoader就是接待室服務自身服務的,他主要是加載JVM自身工作需要的類的,這個lasLoader完全是JVM自己控制的,需要加載那個類,怎么加載都有JVM自己控制,別人也訪問不到這個類,所以這個lasLoader是不遵守前面介紹加載規(guī)則的,他僅僅是作為一個類的加載工具而已。它既沒有更高一級的父加載器,也沒有子類加載器。ExtClassLoaderJVM中自身的一部分,但是他的血統(tǒng)也不是很純正,他并不是JVM親自實現的,我們可以理解為這個類加載器是為那些與這個大會合作單位的員工會員,這些會員既不是JVM內部的,ApplasLoader,這個類加載器就是專門為接待會員服務的,他的父類是ExtlasLoader。他服務的目標就是廣大普通會員,所有在這個Sytem.getProperty(java.clas.path")目錄下的類都可以被這個類加載器加載,這個目錄就是我們經常用到的claspath。如果我們要實現自己的類加載器的話,不管你直接實現抽象類ClassLoader還是繼承URLClassLoader或者其他子類,他的父加載器都是AppClassLoadergetSystemClassLoader()作為父加載器。而getSystemClassLoader方法獲取到的正式AppClassLoaderclassLoader我看到很多的文章中介紹ClassLoader的等級結構在,很多把BootstrapClassLoader也列在ExtClassLoader的上一級中,其實BootstrapClassLoader并不屬于JVM的類等級層次中,因為BootstrapClassLoader并沒有遵守ClassLoader的加載規(guī)則,另外BootstrapClassLoader并沒有子類,ExtClassLoader的父類也不是BootstrapClassLoader,ExtClassLoader并沒有父類,我們應用中能取到的頂層父類是ExtClassLoader這個類。ExtClassLoaderAppClassLoadersunmisc.LauncherLauncherClassLoaderExtClassLoader和AppClassLoader都繼承了URLClassLoader類,而URLClassLoader又實現了抽象類ClassLoader,在LauncherExtClassLoaderExtClassLoaderAppClassLoader對象,而Launcher.getClassLoader()ClassLoaderAppClassLoaderJava應用中沒有定義其他ClassLoader,那么除了System.getProperty("java.ext.dirs")目錄下的類是有ExtClassLoader加載外,其他類都是由AppClassLoader來加載。JVMClassClassLoader來加載需要的類,而是通過JVM加載需要的類到內存的方式就是隱式加載。比如當我們在類中通過繼承或者引用某個類時,JVM在解析當前這個類時發(fā)現引用的類不在內存中,那么就會自動的將這些類加載到內存中。顯示加載:相反的顯示加載就是我們在代碼中通過調用ClassLoader類來加載一個類的方式,如調用this.getClass.getClassLoader().loadClass或者Class.forName(,或者我們自己實現的ClassLoader的findClass方法等其實這兩種方式是混合使用的,如我們通過自定義的ClassLoader顯示加載一個類時,這個類中又引用了其他類,那么這如何ClassLoaderClassLoaderclassClassLoaderclassJVMJVM 第二個階段是有分為三個步驟,分別是自己碼驗證、Class類數據結構分析以及相應的內存分配和最后的符號表的鏈接第三個階段是類中靜態(tài)屬性和初始化賦值,以及靜態(tài)塊的執(zhí)行等。其實在抽象類ClassLoader中并沒有定義如何去loadClass,如何去找到指定類并且把他的字節(jié)碼加載到內存要子類去實findClass方法。我們看一下子類URLClassLoader是如何實現findClass的,在URLClassLoader中是通過一個URLClassPath類幫助取得要加載class文件自己流的,而這個URLClassPath定義了到哪里去找這個class文件,如果找到了這個class文件,再讀取他的byte字節(jié)流通過調研defineClass來創(chuàng)建類Class對象。這個實現機制如同在JavaI/O一章中介紹的InputStream和OutputStream一樣只是定義了讀取文件的機制和形式,并沒我們再看看URLClassLoader類的構造函數,發(fā)現必須要指定一個URL的數據才能夠創(chuàng)建URLClassLoader對象,也就是必須要指定這個ClassLoader默認到那個目錄下去查找class文件。URLURLClassPath對象的必要條件。URLClassPathURLClassPath在創(chuàng)建URLClassPath對象時會根據傳過來的URL數組的中的路徑來判斷是文件或者是jar包,根據路徑的不同分別創(chuàng)FileLoaderJarLoaderLoaderJVMfindClassLoaderclass文件的字節(jié)如何設置每個ClassLoader的搜索路徑呢?以下是設置BootstrapClassLoaderExtClassLoader和AppClassLoader的參數ClassLoaderBootstrap-BootstrapClassLoader--BootstrapClassLoaderBootstrapClassLoaderExtClassLoader-cp或-AppClassLoader上面的參數設置中,我們最常用到的就是設置classpath的環(huán)境變量,因為通常都是讓Java運行我們指定的程序。如果你通過命令行執(zhí)行一個類時出現NoClassDefFoundError錯誤,那么很可能是你沒有指定classpath所致,或者你指定了classpath但是你沒有指明包名,關于ClassLoader的出錯分析在后面詳細介紹。 類準備 常見loaderClass錯誤分析JavaClassNotFoundExceptionNoClassDefFoundError這兩個異常,他們都是和類加載有關,下ClassNotFoundException異??峙率荍ava程序員經常碰到的異常,尤其是對剛剛初學者來說,簡直讓人崩潰,命名那個類就在這里,為啥就是找不到呢?恐怕無數個Java程序員,問過自己無數次。publicpublicclassnotfountexceptionpublicstaticvoidmain(String[]{try}catch(ClassNotFoundException{}}}ClassforName()ClassLoaderloadClass()ClassLoaderfindSystemClass()出現這類錯誤也很好理解,就職JVM要加載你指定的文件的字節(jié)碼到內存時,而并沒有找到這個文件對應的字節(jié)碼,也就是這個文件并不存在。解決的辦法就是檢查當前的classpath目錄下有沒有指定的文件存在。如果不知道當前classpath路徑的話,可以通過如下命令來獲?。? NoClassDefFoundError也是另外一個經常遇到的異常,這個異常在第一次使用命令行執(zhí)行Java類時,很可能會用到, java–cpexample.jar 而這個jar包里面就一個類,這類是net.xulingbo.Example,你可能會感到郁悶明明這個jar包里有這個類為啥會報如下ExceptionExceptioninthread"main"java.lang.NoClassDefFoundError:example/jarCausedby:java.lang.ClassNotFoundException:example.jaratatjava.security.AccessController.doPrivileged(NativeMethod).URLClassLoader.findClass(URLClassLoader.java:188)atjava.lang.ClassLoader.loadClass(ClassLoader.java:306)atsun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)atjava.lang.ClassLoader.loadClass(ClassLoader.java:251)atjavajava–cpexample.jarNoClassDefFoundErrorClassNotFoundExceptionjavaexample.jar后在顯示加載Example這個類是沒有找到,所以是ClassNotFoundException引發(fā)了NoClassDefFoundError異常。JVM的規(guī)范中描述了出現NoClassDefFoundError可能的情況就是通過new關鍵字、屬性引用每個類、繼承了某個接口或JVM隱式的加載這些類時發(fā)現這些類不存在就會報這個異常。解決這個錯誤的辦法就是確保每個類引用的類都在當前的classpathJVMJVMlib刪除的話,publicpublicclassNoLibExceptionpublicnativevoidnativeMethod();static{}publicstaticvoidmain(String[]{new}}這個錯誤通常都是在解析native標識的方法時,如果JVMExceptionExceptioninthread"main"java.lang.UnsatisfiedLinkError:noNoLibinjava.library.pathatjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)atjava.lang.System.loadLibrary(System.java:1084)atjava.lang.Class.forName0(NativeMethod)atatpublicpublicclassCastException{publicstaticMapm=newHashMap(){{publicstaticvoidmain(String[]args)IntegerIntegerisInt=(Integer)m.get("a");}}IntegerIntegerExceptionExceptioninthread"main"java.lang.ClassCastException:java.lang.Stringcannotbecasttojava.lang.IntegeratEmptyProject.classloader.CastException.main(CastException.java:17)atatjava.lang.reflect.Method.invoke(Method.java:613)at 對于普通對象:對象必須是目標類的實例或目標類的子類的實例。如果目標類是接口,那么會把它當作實現了該接口的一個子類。 對于數組類型:目標類必須是數組類型或java.lang.Object、java.lang.Cloneable或java.io.Serializable。如果沒有滿足上面的規(guī)則的話,JVM就會報這個錯誤了,要避免這個錯誤有兩種方式:在容器類型中顯示的指明這個容器所包含的對象類型如上面的Map中可以樣:Map<String,Integer>m=new第二種方式是先通過instanceofJVM如果Java 虛擬機試圖創(chuàng)建類ExceptionInInitializerError的新實例,但是因為出現Out-Of-Memory-Error而無法創(chuàng)建新實例,那么就拋出OutOfMemoryError對象作為代替。如果初始化器突然完成,拋出一些異常E,而且E的類不是Error或者它的某個子類,那么就會創(chuàng)建ExceptionInInitializerErrorEE。publicpublicclassCastException{publicstaticMapm=newHashMap(){{publicstaticvoidmain(String[]{IntegerisInt=(Integer)m.get("a");}}ExceptionExceptioninthread"main"java.lang.ExceptionInInitializerErroratjava.lang.Class.forName0(NativeMethod)atatCausedby:atatat...3當在初始化這個類時給靜態(tài)屬性m賦值的時候出現了異常就會導致拋ExceptionInInitializerErrorClassLoader前面分析 的工作機制,我們下面再看看一些開源的框架是如何根據這種工作機制來設置自己ClassLoaderClassLoaderwebHelloWorldServletServletClassLoader,代publicpublicclassHelloWorldServletextendsHttpServletpublicvoiddoGet(HttpServletRequestHttpServletResponseresponse)ClassLoaderclassLoader=while(classLoader!=null){classLoader=classLoader.getParent();}}}將這個應用通過<context/>方式配置在server.xml<Host<Hostname="localhost"appBase="webapps"unpackWARs="true"tomcat的webappswebapps目錄下,tomcat使用的ServletClassLoaderTomcatClassLoaderTomcatClassLoaderStandardClassLoaderStandardClassLoader是在Bootstrap類的initClassLoaders方法中創(chuàng)建的,Bootstrap調用ClassLoaderFactory的createClassLoader方法創(chuàng)建StandardClassLoader對象,如果沒有指定StandardClassLoader類的父ClassLoader的話,景默認設置getSystemClassLoader()方法返回的ClassLoader作為其父類,getSystemClassLoader()返回的ClassLoader通常就是AppClassLosder。StandardClassLoader創(chuàng)建成功的話,將設置到Bootstrap的catalinaLoader屬性中作為整個Tomcat的根ClassLoader。接下去Tomcat將以StandardClassLoader來加載org.apache.catalina.startup.CatalinaStandardClassLoaderCatalinaparentClassLoaderTomcatClassLoader都將是Tomcat容器的加載lasLoader是tandardlassLoaderTomcat中任何一個類如StandardContextgetlas().getClasLoader()方法返回的lassLoader并不是tandardClassLoader,而是pplassLoader,為什么呢?原因是tandardlassLoader雖然是加載tandardContext的類,但是可以看一下tandardlasLoader的實現方法,可以發(fā)現tandardClassLoader只是一個代理類并沒有覆蓋lasLoader的loadlas方法,tandardClasLoader仍然是沿用委托加載機器,他會首先會用父加載器來加載,所以真正加載類的仍然是通過其父類ApplassLoader完成的,所以加載Tomcat容器本身的仍然是ApplasLoader這個lasLoader。TomcatClassPathAppClassLoaderTomcat容器的類,這時就要通過StandardClassLoader來加載了。其實不管是StandardClassLoader還是AppClassLoader加載都沒有任何影響,因為他們其實我們正在關心的不是Tomcat容器本身是誰加載的,而更關心的是我們的應用是怎么加載的,也就是我們一個應用需要Tomcat我們知道一個應用在Tomcat中由一個StandardContext表示,由StandardContext來解釋web應用的web.xml配置文件來實例化所有的Servlet。Servlet的class是在<servlet-class>來指定,所以可想而知每個Servlet類的加載肯定是通過顯示加載方法加載到Tomcat容器中的。那么Servlet是如何被加載的呢?先看看StandardContext類startInternal方法,在StandardContext初始化時將會檢查loaderifif(getLoader()==null)WebappLoaderwebappLoader=newWebappLoader(getParentClassLoader());}這段代碼清楚的表示將創(chuàng)建WebappLoader對象,而WebappLoader對象的將創(chuàng)建WebappClassLoader作為其ClassLoaderStandardWrapperloadServletServletInstanceManager來實例化的,InstanceManager類使用的ClassLoader是不是WebappClassLoader呢?在看一下InstanceManagerpublicpublicDefaultInstanceManager(Contextcontext,Map<String,Map<String,String>>injectionMap,org.apache.catalina.ContextcatalinaContext,ClassLoadercontainerClassLoader){classLoader=catalinaContext.getLoader().getClassLoader();privileged=catalinaContext.getPrivileged();this.containerClassLoader=containerClassLoader;…}InstanceManager對象的ClassLoader也是獲取StandardContext的Loader中的ClassLoader,也就是前面設置的WebappClassLoader。所以所以ServletClassLoaderWebappClassLoader。WebappClassLoader不像StandardClassLoader那么簡單了,他覆蓋了父類的loadClass方法,使用自己的加載機制,這首先檢查WebappClassLoader中是否已經加載過了,如果請求的類以前是被WebappClassLoader加載的,那么肯定在WebappClassLoader的緩存容器resourceEntries中。如果不在WebappClassLoader的resourceEntries中,繼續(xù)檢查JVM虛擬機中是否已經加載過,也就是調用ClassLoader的findLoadedClass方法。如果前兩個緩存中都沒有的話,將先調用 加載請求的類 在這里也就AppClassLoaderJVM的ClassPath檢查請求的類是否在packageTriggers定義的包名下,如果在這個設置的包目錄下,將通過StandardClassLoader類如果還沒有找到的話將由WebappClassLoader來加載了,WebappClassLoader將會在這個應用的WEB-INF/classes目錄下查找請求的類文件的字節(jié)碼。找到后將創(chuàng)建一個ResourceEntry對象保存這個類的元信息。并把它保存在WebappClassLoader的resourceEntries容器中便于下次查找。接著將調用defineClass方法生成請求類的Class對象并返回給InstanceManager來創(chuàng)建實例。關于Servlet的創(chuàng)建過程將在后面的章節(jié)中詳細介紹。從上面的分析來看Tomcat仍然沿用了JVM的類加載規(guī)范也就是委托式加載,保證核心類是通過AppClassLoader來加載。但是Tomcat會優(yōu)先檢查WebappClassLoader已經加載的緩存,而不是JVM的findLoadedClass緩存。這一點需要這也回答了如果你將一個web應用直接方法webapp目錄下,那么Tomcat就是通過StandardClassLoader直接加載了,而不是通過WebappClassLoader來加載。OSGi是Java上的動態(tài)模塊系統(tǒng)。它為開發(fā)人員提供了面向服務和基于組件的運行環(huán)境,并提供標準的方式用來管理軟件的生命周期。OSGi已經被實現和部署在很多產品上,在開源社區(qū)也得到了廣泛的支持。Eclipse就是基于OSGi技和類(通過Import-Package,也可以聲明導出(export)自己的包和類,供其它模塊使用(Export-Package。也就JavaOSGi特有的類加載器機制來實現的。OSGi中的每個模塊都有對應的一個類加載器。它負責加載模塊自己包含的Java包和類。當它需要加載Java核心庫的類時它會代理給導出此Java類的模塊來完成加載。模塊也可以顯式的聲明某些Java包和類,必須由父類加載器來加載。只需要設置系統(tǒng)屬性org.osgiframework.bootdelegation的值即可。假設有兩個模塊bundleA和bundle,它們都有自己對應的類加載器classLoaderA和classLoaderB。在bundleA中包含類com.bundle.Sample,并且該類被聲明為導出的,也就是說可以被其它模塊所使用的。bundleB聲明了導入bundleA提供的類com.bundle.ample,并包含一個類com.bundle.ewSample繼承自com.bundle.Sample。在bundleB啟動的時候,其類加載器classLoaderB需要加載類com.bundle.ewSample,進而需要加載類com.bundleA.ample。由于bundleB聲明了類com.bundle.Sample是導入的,classLoaderB把加載類com.bundleA.ample的工作代理給導出該類的bundleA的類加載器clasLoaderAclassLoaderA在其模塊內部查找類com.bundleA.ample并定義它,所得到的類com.bundleA.ample實例就可以被所有聲明導入了此類的模塊使用。對于以java開頭的類,都是由父類加載器來加載的。如果聲明了系統(tǒng)屬性org.osgi.framework.bootdelegation=com.example.core.*com.example.core中的類,都是由父類加載器來完成的。OSGi模塊的這種類加載器結構,使得一個類的不同版本可以共存在Java如果一個類庫只有一個模塊使用,把該類庫的 包放在模塊中,在Bundle-ClassPath中指明即可如果一個類庫被多個模塊共用,可以為這個類庫單獨的創(chuàng)建一個模塊,把其它模塊需要用到的Java包聲明為導出的。如果類庫提供了SPI接口,并且利用線程上下文類加載器來加載SI實現的Java類,有可能會找不到Java類。如果出現了NolassDefFoundError異常,首先檢查當前線程的上下文類加載器是否正確。通過Thread.currentThread().getContextlasLoader()就可以得到該類加載器。該類加載器應該是該模塊對應的類加載器。如果不是的話,可以首先通過clas.getlasLoader()來得到模塊對應的類加載器,再通過Thread.currentThread().etContextlassLoader()來設置當前線程的上下文類加載器。 classclassClassPath下面,那么我們自己必須想辦法來找到這個類,這種情況下我們需要自己實現一個ClassLoader。對我們自己的要加載的類做特殊處理,如保證通過網絡傳輸的類的安全性,可以對類經過加密后在傳輸,那種在加載到JVM之前需要對類的自己碼再解密,這個過程就可以在自定義的lassLoader中實現??梢远x類的實效機制,如果我們可以檢查已經加載的 下面就這幾種情況來創(chuàng)建自己的我們自己實現一個ClassLoader,并制定這個ClassLoaderpublicclassPathClassLoaderextendsClassLoader{publicclassPathClassLoaderextendsClassLoader{privateStringclassPath;{this.classPath=}protectedClass<?>findClass(Stringname)throws{if(packageName.startsWith(name)){byte[]classData=getData(name);if(classData==null){thrownew}elsereturndefineClass(name,classData,0,}return}}privatebyte[]getData(StringclassName)try{InputStreamis=newFileInputStream(path);ByteArrayOutputStreamstream=newByteArrayOutputStream();byte[]buffer=newbyte[2048];intnum=while((num=is.read(buffer))!=-{stream.write(buffer,0,}return}catch(IOException{}return}}上面這段代碼中從classPath目錄下去加載指定包名的class文件,如果不是“net.xulingbo.classloader”的話仍然使用父還有一種方式是繼承URLClassLoader類,然后設置自定以路徑的URL,來加載URL下的類,這種方式更加常見,如publicclassURLPathClassLoaderextendsURLClassLoader{publicclassURLPathClassLoaderextendsURLClassLoader{privateStringpackageName={}protectedClass<?>findClass(Stringname)throws{Class<?>aClass=findLoadedClass(name);if(aClass!=null){return}ifreturn}}}我們將指定的目錄轉化成URL路徑然后作為參數創(chuàng)建URLPathClassLoader對象,那個這個ClassLoader在加載的時候就是在URL指定的目錄下查找指定的類文件。假設我們通過網絡從遠處主機上下載一個class文件的字節(jié)碼,但是我們?yōu)榱税踩?,在傳輸之前對這個字節(jié)碼進行了簡單的加密處理,然后在通過網絡傳輸。當客戶端接收到這個類的字節(jié)碼后需要經過解密才能還原成原始的類格式,然后在通過lasLoader的definelass方法創(chuàng)建這個類Class實例,最后完成類的加載工作,這個代碼如下所示:publicclassNetClassLoaderextends{privateStringprivateStringpackageName={this.classPath=}protectedClass<?>findClass(Stringname)throws{Class<?>aClass=findLoadedClass(name);if(aClass!=null){return}if{byte[]classData=getData(name);if(classData==null){thrownew}elsereturndefineClass(name,classData,0,}}elsereturn}}privatebyte[]getData(StringclassName)Stringpath=classPath+File.separatorChar+className.replace('.',File.separatorChar)+".class";try{URLurl=newURL(path);InputStreamis=url.openStream();ByteArrayOutputStreamstream=newByteArrayOutputStream();byte[]buffer=newbyte[2048];intnum=while((num=is.read(buffer))!=-{stream.write(buffer,0,}return}catch(Exception{}return}privatebyte[]deCode(byte[]{byte[]decode=return}在方法deCode中可以對網絡傳輸過來的字節(jié)碼進行某種解密處理,然后返回正確的class字節(jié)碼調用defineClass來創(chuàng)建Class對象。我們知道JVM在加載類之前會檢查請求的類是否已經被加載過來,也就是要調用findLoadedClass方法看是否能夠返回Class實例。如果類已經加載過來在調用loadClass將會導致類沖突。但是JVM表示一個類是否是同一個類會有兩個條件。一個是看這個類的完整類名是否一樣,這個類名包括類所在的包名。還有一個是加載這個類的ClassLoader是否是同一個,這里所說的同一個是指ClassLoader的實例是否是同一個實例。如果是同一個ClassLoader類的兩個實例加載同一個類也會不一樣。所以要實現類的熱部署那么可以通過創(chuàng)建不同的ClassLoader的實例對象,然后通過這個不同的實例publicpublicclassClassReloaderextends{privateStringclassPath;Stringclassname="compile.Yufa";{this.classPath=}protectedClass<?>findClass(Stringname)throws{byte[]classData=getData(name);if(classData==null){thrownew}elsereturndefineClass(classname,classData,0,}}privatebyte[]getData(String{Stringpath=classPath+className;try{InputStreamis=newFileInputStream(path);ByteArrayOutputStreamstream=newByteArrayOutputStream();byte[]buffer=newbyte[2048];intnum=while((num=is.read(buffer))!=-{stream.write(buffer,0,}return}catch(IOException{}return}publicstaticvoidmain(String[]{tryStringpath="D:/devtools/compile/target/classes/compi

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論