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

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

ClassLoaderClassLoaderClassJVM賓發(fā)放入會證明,入會的嘉賓分為VIP會員、黃金會員、白金會員和普通會員等。對應(yīng)的接待室也會分VIP會員接待室、黃金會員接待室白金會員接待室和普通接待室,不同等級的會員會被不同的接待室接待。所有的會員要想進(jìn)入會場只有位大佬但是你不是VIP接待室接待的,那么對不起你仍然不是VIP會員。當(dāng)然你是不是VIP會員,會有嚴(yán)格的審查規(guī)ClassLoaderClassJVM中之外,還有一個重要的作用就是,審查每個類應(yīng)該由誰加載,他是一種ClassLoader除了上述兩個作用外還有一個任務(wù)就是將Class字節(jié)碼重新解析成JVM統(tǒng)一要求的對象格式。這個格式是什么將會在后面的Class文件格式分析一章本章將主要分析CLassLoader的前兩個作用:也就是ClassLoader的加載機(jī)制和如何加載類的過程。另外還將著重介紹JavaWeb中常用的ClassLoader是如何實現(xiàn)的,理解他們將幫助我們在日常的開發(fā)過程中更加理解我們的程序是如果工作ClassLoader如下圖所示,我們經(jīng)常用到或擴(kuò)展 ClassLoader其中defineClass方法是用來將byte字節(jié)流解析成JVM能夠識別的Class對象,有了這個方法意味著我們不僅僅可以通過class文件實例化對象,還可以通過其他方式如我們通過網(wǎng)絡(luò)接收到一個類的字節(jié)碼,拿這個字節(jié)碼流直接創(chuàng)建類的Class對象形式。注意如果直接調(diào)用這個方法生成的類的Class對象,這個類的Class對象還沒有resolve,這個resolvedefineClass通常是和findClass方法一起使用的,我們通過會直接覆蓋ClassLoader父類的findClass方法,實現(xiàn)我們自己的類的加載規(guī)則,然后取得我們要加載類的字節(jié)碼,然后調(diào)用defineClass方法生成類的Class對象,如果你想在類被加載到JVM中時就希望這個類被LinkresolveClassJVM來link這個類。如果你不想重新定義加載類的規(guī)則,也沒有復(fù)雜的處理邏輯,只想在運行時能夠加載自己制定的一個類而已,那么你可以這樣thi.getClas().getlassLoader().loadlas("clas")調(diào)用lasLoader的loadlass方法就可以獲取這個類的lass對象,這個loadClas還有重載方法你同樣可以決定在什么時候reolve這個clas。ClassLoader是個抽象類,他還有很多子類,我們?nèi)绻獙崿F(xiàn)自己的ClassLoader的話,一般都會繼承ServletHttpServlet前面介紹的這幾個方法都是我們要擴(kuò)展ClassLoader時需要打交道的,ClassLoader還提供了另外一些輔助方法,如獲取class文件的方法如getResource以及getResourceAsStream等。還有就是獲取SystemClassLoader的方法等,關(guān)于這些方ClassLoader在前面的會員進(jìn)入會場的規(guī)則中,如何保證不同等級的會員通過不同的會員接待室接待進(jìn)入會場?因為可能存在一些會員可能并不能自己就能正確的找到接待自己的接待室的情況,也有可能有些會員會冒充更高級的會員身份混進(jìn)去這種情況,所以必須要有機(jī)制能夠保證所有會員都被正確的接待室接待進(jìn)入會場,而且一個會員只能被一個接待室接待,不能出現(xiàn)被兩個接待室重復(fù)接待的情況出現(xiàn),也就是不能同時拿到兩個入場證明,也就是要保證接待的一致性。如何設(shè)計這個接待規(guī)則呢?lassLoader就設(shè)計了這樣一致接待機(jī)制,這個機(jī)制就是上級委托接待機(jī)制。他是這樣的:任何一個會員到達(dá)任何一個會員接待室時,這個接待室首先會檢查這個會員是否已經(jīng)被自己接待過,如果已經(jīng)接待過的話,則拒絕本次接待也就是不再發(fā)入會證明了,如果沒有接待過的話,那么會向上詢問,這個會員是否應(yīng)該在上一級的更高級別的接待室接待,上級接待室會根據(jù)他們的接待規(guī)則,檢查這個會員是否被已經(jīng)接到過,如果已經(jīng)接待過同樣的處理方法,將已經(jīng)接待的結(jié)果反饋給下一級,如果也沒有接待過的話,他再向更高一級(如果有更高一級的話)接到室轉(zhuǎn)發(fā)接待請求,更高一級也是同樣的處理方法,知道有一級接待接待或者告訴他下一級,這個會員不是自己接待這個結(jié)果。如果這個會員來到的這個接待室得到他上一級的接待室反饋這個會員沒有被接待,并且也不應(yīng)該是由他們接待的話,這個接待室將會正式接待這個會員,并發(fā)給他入會證明,那么這個會員就被定義為這個接待室等級的會員身份。這種接待規(guī)則看上去有點麻煩,但是他卻能夠保證所有的會員能夠被正確的接待室接待,會員的身份也不會錯誤,也不存在冒充身份的會員。JVMClassLoaderClassLoader可以分為兩種類型,可以理解為為接待室服務(wù)的接待室和BoottraplasLoader,這個lasLoader就是接待室服務(wù)自身服務(wù)的,他主要是加載JVM自身工作需要的類的,這個lasLoader完全是JVM自己控制的,需要加載那個類,怎么加載都有JVM自己控制,別人也訪問不到這個類,所以這個lasLoader是不遵守前面介紹加載規(guī)則的,他僅僅是作為一個類的加載工具而已。它既沒有更高一級的父加載器,也沒有子類加載器。ExtClassLoaderJVM中自身的一部分,但是他的血統(tǒng)也不是很純正,他并不是JVM親自實現(xiàn)的,我們可以理解為這個類加載器是為那些與這個大會合作單位的員工會員,這些會員既不是JVM內(nèi)部的,ApplasLoader,這個類加載器就是專門為接待會員服務(wù)的,他的父類是ExtlasLoader。他服務(wù)的目標(biāo)就是廣大普通會員,所有在這個Sytem.getProperty(java.clas.path")目錄下的類都可以被這個類加載器加載,這個目錄就是我們經(jīng)常用到的claspath。如果我們要實現(xiàn)自己的類加載器的話,不管你直接實現(xiàn)抽象類ClassLoader還是繼承URLClassLoader或者其他子類,他的父加載器都是AppClassLoadergetSystemClassLoader()作為父加載器。而getSystemClassLoader方法獲取到的正式AppClassLoaderclassLoader我看到很多的文章中介紹ClassLoader的等級結(jié)構(gòu)在,很多把BootstrapClassLoader也列在ExtClassLoader的上一級中,其實BootstrapClassLoader并不屬于JVM的類等級層次中,因為BootstrapClassLoader并沒有遵守ClassLoader的加載規(guī)則,另外BootstrapClassLoader并沒有子類,ExtClassLoader的父類也不是BootstrapClassLoader,ExtClassLoader并沒有父類,我們應(yīng)用中能取到的頂層父類是ExtClassLoader這個類。ExtClassLoaderAppClassLoadersunmisc.LauncherLauncherClassLoaderExtClassLoader和AppClassLoader都繼承了URLClassLoader類,而URLClassLoader又實現(xiàn)了抽象類ClassLoader,在LauncherExtClassLoaderExtClassLoaderAppClassLoader對象,而Launcher.getClassLoader()ClassLoaderAppClassLoaderJava應(yīng)用中沒有定義其他ClassLoader,那么除了System.getProperty("java.ext.dirs")目錄下的類是有ExtClassLoader加載外,其他類都是由AppClassLoader來加載。JVMClassClassLoader來加載需要的類,而是通過JVM加載需要的類到內(nèi)存的方式就是隱式加載。比如當(dāng)我們在類中通過繼承或者引用某個類時,JVM在解析當(dāng)前這個類時發(fā)現(xiàn)引用的類不在內(nèi)存中,那么就會自動的將這些類加載到內(nèi)存中。顯示加載:相反的顯示加載就是我們在代碼中通過調(diào)用ClassLoader類來加載一個類的方式,如調(diào)用this.getClass.getClassLoader().loadClass或者Class.forName(,或者我們自己實現(xiàn)的ClassLoader的findClass方法等其實這兩種方式是混合使用的,如我們通過自定義的ClassLoader顯示加載一個類時,這個類中又引用了其他類,那么這如何ClassLoaderClassLoaderclassClassLoaderclassJVMJVM 第二個階段是有分為三個步驟,分別是自己碼驗證、Class類數(shù)據(jù)結(jié)構(gòu)分析以及相應(yīng)的內(nèi)存分配和最后的符號表的鏈接第三個階段是類中靜態(tài)屬性和初始化賦值,以及靜態(tài)塊的執(zhí)行等。其實在抽象類ClassLoader中并沒有定義如何去loadClass,如何去找到指定類并且把他的字節(jié)碼加載到內(nèi)存要子類去實findClass方法。我們看一下子類URLClassLoader是如何實現(xiàn)findClass的,在URLClassLoader中是通過一個URLClassPath類幫助取得要加載class文件自己流的,而這個URLClassPath定義了到哪里去找這個class文件,如果找到了這個class文件,再讀取他的byte字節(jié)流通過調(diào)研defineClass來創(chuàng)建類Class對象。這個實現(xiàn)機(jī)制如同在JavaI/O一章中介紹的InputStream和OutputStream一樣只是定義了讀取文件的機(jī)制和形式,并沒我們再看看URLClassLoader類的構(gòu)造函數(shù),發(fā)現(xiàn)必須要指定一個URL的數(shù)據(jù)才能夠創(chuàng)建URLClassLoader對象,也就是必須要指定這個ClassLoader默認(rèn)到那個目錄下去查找class文件。URLURLClassPath對象的必要條件。URLClassPathURLClassPath在創(chuàng)建URLClassPath對象時會根據(jù)傳過來的URL數(shù)組的中的路徑來判斷是文件或者是jar包,根據(jù)路徑的不同分別創(chuàng)FileLoaderJarLoaderLoaderJVMfindClassLoaderclass文件的字節(jié)如何設(shè)置每個ClassLoader的搜索路徑呢?以下是設(shè)置BootstrapClassLoaderExtClassLoader和AppClassLoader的參數(shù)ClassLoaderBootstrap-BootstrapClassLoader--BootstrapClassLoaderBootstrapClassLoaderExtClassLoader-cp或-AppClassLoader上面的參數(shù)設(shè)置中,我們最常用到的就是設(shè)置classpath的環(huán)境變量,因為通常都是讓Java運行我們指定的程序。如果你通過命令行執(zhí)行一個類時出現(xiàn)NoClassDefFoundError錯誤,那么很可能是你沒有指定classpath所致,或者你指定了classpath但是你沒有指明包名,關(guān)于ClassLoader的出錯分析在后面詳細(xì)介紹。 類準(zhǔn)備 常見loaderClass錯誤分析JavaClassNotFoundExceptionNoClassDefFoundError這兩個異常,他們都是和類加載有關(guān),下ClassNotFoundException異??峙率荍ava程序員經(jīng)常碰到的異常,尤其是對剛剛初學(xué)者來說,簡直讓人崩潰,命名那個類就在這里,為啥就是找不到呢?恐怕無數(shù)個Java程序員,問過自己無數(shù)次。publicpublicclassnotfountexceptionpublicstaticvoidmain(String[]{try}catch(ClassNotFoundException{}}}ClassforName()ClassLoaderloadClass()ClassLoaderfindSystemClass()出現(xiàn)這類錯誤也很好理解,就職JVM要加載你指定的文件的字節(jié)碼到內(nèi)存時,而并沒有找到這個文件對應(yīng)的字節(jié)碼,也就是這個文件并不存在。解決的辦法就是檢查當(dāng)前的classpath目錄下有沒有指定的文件存在。如果不知道當(dāng)前classpath路徑的話,可以通過如下命令來獲?。? NoClassDefFoundError也是另外一個經(jīng)常遇到的異常,這個異常在第一次使用命令行執(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ī)范中描述了出現(xiàn)NoClassDefFoundError可能的情況就是通過new關(guān)鍵字、屬性引用每個類、繼承了某個接口或JVM隱式的加載這些類時發(fā)現(xiàn)這些類不存在就會報這個異常。解決這個錯誤的辦法就是確保每個類引用的類都在當(dāng)前的classpathJVMJVMlib刪除的話,publicpublicclassNoLibExceptionpublicnativevoidnativeMethod();static{}publicstaticvoidmain(String[]{new}}這個錯誤通常都是在解析native標(biāo)識的方法時,如果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 對于普通對象:對象必須是目標(biāo)類的實例或目標(biāo)類的子類的實例。如果目標(biāo)類是接口,那么會把它當(dāng)作實現(xiàn)了該接口的一個子類。 對于數(shù)組類型:目標(biāo)類必須是數(shù)組類型或java.lang.Object、java.lang.Cloneable或java.io.Serializable。如果沒有滿足上面的規(guī)則的話,JVM就會報這個錯誤了,要避免這個錯誤有兩種方式:在容器類型中顯示的指明這個容器所包含的對象類型如上面的Map中可以樣:Map<String,Integer>m=new第二種方式是先通過instanceofJVM如果Java 虛擬機(jī)試圖創(chuàng)建類ExceptionInInitializerError的新實例,但是因為出現(xiàn)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當(dāng)在初始化這個類時給靜態(tài)屬性m賦值的時候出現(xiàn)了異常就會導(dǎo)致拋ExceptionInInitializerErrorClassLoader前面分析 的工作機(jī)制,我們下面再看看一些開源的框架是如何根據(jù)這種工作機(jī)制來設(shè)置自己ClassLoaderClassLoaderwebHelloWorldServletServletClassLoader,代publicpublicclassHelloWorldServletextendsHttpServletpublicvoiddoGet(HttpServletRequestHttpServletResponseresponse)ClassLoaderclassLoader=while(classLoader!=null){classLoader=classLoader.getParent();}}}將這個應(yīng)用通過<context/>方式配置在server.xml<Host<Hostname="localhost"appBase="webapps"unpackWARs="true"tomcat的webappswebapps目錄下,tomcat使用的ServletClassLoaderTomcatClassLoaderTomcatClassLoaderStandardClassLoaderStandardClassLoader是在Bootstrap類的initClassLoaders方法中創(chuàng)建的,Bootstrap調(diào)用ClassLoaderFactory的createClassLoader方法創(chuàng)建StandardClassLoader對象,如果沒有指定StandardClassLoader類的父ClassLoader的話,景默認(rèn)設(shè)置getSystemClassLoader()方法返回的ClassLoader作為其父類,getSystemClassLoader()返回的ClassLoader通常就是AppClassLosder。StandardClassLoader創(chuàng)建成功的話,將設(shè)置到Bootstrap的catalinaLoader屬性中作為整個Tomcat的根ClassLoader。接下去Tomcat將以StandardClassLoader來加載org.apache.catalina.startup.CatalinaStandardClassLoaderCatalinaparentClassLoaderTomcatClassLoader都將是Tomcat容器的加載lasLoader是tandardlassLoaderTomcat中任何一個類如StandardContextgetlas().getClasLoader()方法返回的lassLoader并不是tandardClassLoader,而是pplassLoader,為什么呢?原因是tandardlassLoader雖然是加載tandardContext的類,但是可以看一下tandardlasLoader的實現(xiàn)方法,可以發(fā)現(xiàn)tandardClassLoader只是一個代理類并沒有覆蓋lasLoader的loadlas方法,tandardClasLoader仍然是沿用委托加載機(jī)器,他會首先會用父加載器來加載,所以真正加載類的仍然是通過其父類ApplassLoader完成的,所以加載Tomcat容器本身的仍然是ApplasLoader這個lasLoader。TomcatClassPathAppClassLoaderTomcat容器的類,這時就要通過StandardClassLoader來加載了。其實不管是StandardClassLoader還是AppClassLoader加載都沒有任何影響,因為他們其實我們正在關(guān)心的不是Tomcat容器本身是誰加載的,而更關(guān)心的是我們的應(yīng)用是怎么加載的,也就是我們一個應(yīng)用需要Tomcat我們知道一個應(yīng)用在Tomcat中由一個StandardContext表示,由StandardContext來解釋web應(yīng)用的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,也就是前面設(shè)置的WebappClassLoader。所以所以ServletClassLoaderWebappClassLoader。WebappClassLoader不像StandardClassLoader那么簡單了,他覆蓋了父類的loadClass方法,使用自己的加載機(jī)制,這首先檢查WebappClassLoader中是否已經(jīng)加載過了,如果請求的類以前是被WebappClassLoader加載的,那么肯定在WebappClassLoader的緩存容器resourceEntries中。如果不在WebappClassLoader的resourceEntries中,繼續(xù)檢查JVM虛擬機(jī)中是否已經(jīng)加載過,也就是調(diào)用ClassLoader的findLoadedClass方法。如果前兩個緩存中都沒有的話,將先調(diào)用 加載請求的類 在這里也就AppClassLoaderJVM的ClassPath檢查請求的類是否在packageTriggers定義的包名下,如果在這個設(shè)置的包目錄下,將通過StandardClassLoader類如果還沒有找到的話將由WebappClassLoader來加載了,WebappClassLoader將會在這個應(yīng)用的WEB-INF/classes目錄下查找請求的類文件的字節(jié)碼。找到后將創(chuàng)建一個ResourceEntry對象保存這個類的元信息。并把它保存在WebappClassLoader的resourceEntries容器中便于下次查找。接著將調(diào)用defineClass方法生成請求類的Class對象并返回給InstanceManager來創(chuàng)建實例。關(guān)于Servlet的創(chuàng)建過程將在后面的章節(jié)中詳細(xì)介紹。從上面的分析來看Tomcat仍然沿用了JVM的類加載規(guī)范也就是委托式加載,保證核心類是通過AppClassLoader來加載。但是Tomcat會優(yōu)先檢查WebappClassLoader已經(jīng)加載的緩存,而不是JVM的findLoadedClass緩存。這一點需要這也回答了如果你將一個web應(yīng)用直接方法webapp目錄下,那么Tomcat就是通過StandardClassLoader直接加載了,而不是通過WebappClassLoader來加載。OSGi是Java上的動態(tài)模塊系統(tǒng)。它為開發(fā)人員提供了面向服務(wù)和基于組件的運行環(huán)境,并提供標(biāo)準(zhǔn)的方式用來管理軟件的生命周期。OSGi已經(jīng)被實現(xiàn)和部署在很多產(chǎn)品上,在開源社區(qū)也得到了廣泛的支持。Eclipse就是基于OSGi技和類(通過Import-Package,也可以聲明導(dǎo)出(export)自己的包和類,供其它模塊使用(Export-Package。也就JavaOSGi特有的類加載器機(jī)制來實現(xiàn)的。OSGi中的每個模塊都有對應(yīng)的一個類加載器。它負(fù)責(zé)加載模塊自己包含的Java包和類。當(dāng)它需要加載Java核心庫的類時它會代理給導(dǎo)出此Java類的模塊來完成加載。模塊也可以顯式的聲明某些Java包和類,必須由父類加載器來加載。只需要設(shè)置系統(tǒng)屬性org.osgiframework.bootdelegation的值即可。假設(shè)有兩個模塊bundleA和bundle,它們都有自己對應(yīng)的類加載器classLoaderA和classLoaderB。在bundleA中包含類com.bundle.Sample,并且該類被聲明為導(dǎo)出的,也就是說可以被其它模塊所使用的。bundleB聲明了導(dǎo)入bundleA提供的類com.bundle.ample,并包含一個類com.bundle.ewSample繼承自com.bundle.Sample。在bundleB啟動的時候,其類加載器classLoaderB需要加載類com.bundle.ewSample,進(jìn)而需要加載類com.bundleA.ample。由于bundleB聲明了類com.bundle.Sample是導(dǎo)入的,classLoaderB把加載類com.bundleA.ample的工作代理給導(dǎo)出該類的bundleA的類加載器clasLoaderAclassLoaderA在其模塊內(nèi)部查找類com.bundleA.ample并定義它,所得到的類com.bundleA.ample實例就可以被所有聲明導(dǎo)入了此類的模塊使用。對于以java開頭的類,都是由父類加載器來加載的。如果聲明了系統(tǒng)屬性org.osgi.framework.bootdelegation=com.example.core.*com.example.core中的類,都是由父類加載器來完成的。OSGi模塊的這種類加載器結(jié)構(gòu),使得一個類的不同版本可以共存在Java如果一個類庫只有一個模塊使用,把該類庫的 包放在模塊中,在Bundle-ClassPath中指明即可如果一個類庫被多個模塊共用,可以為這個類庫單獨的創(chuàng)建一個模塊,把其它模塊需要用到的Java包聲明為導(dǎo)出的。如果類庫提供了SPI接口,并且利用線程上下文類加載器來加載SI實現(xiàn)的Java類,有可能會找不到Java類。如果出現(xiàn)了NolassDefFoundError異常,首先檢查當(dāng)前線程的上下文類加載器是否正確。通過Thread.currentThread().getContextlasLoader()就可以得到該類加載器。該類加載器應(yīng)該是該模塊對應(yīng)的類加載器。如果不是的話,可以首先通過clas.getlasLoader()來得到模塊對應(yīng)的類加載器,再通過Thread.currentThread().etContextlassLoader()來設(shè)置當(dāng)前線程的上下文類加載器。 classclassClassPath下面,那么我們自己必須想辦法來找到這個類,這種情況下我們需要自己實現(xiàn)一個ClassLoader。對我們自己的要加載的類做特殊處理,如保證通過網(wǎng)絡(luò)傳輸?shù)念惖陌踩?,可以對類?jīng)過加密后在傳輸,那種在加載到JVM之前需要對類的自己碼再解密,這個過程就可以在自定義的lassLoader中實現(xiàn)??梢远x類的實效機(jī)制,如果我們可以檢查已經(jīng)加載的 下面就這幾種情況來創(chuàng)建自己的我們自己實現(xiàn)一個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類,然后設(shè)置自定以路徑的URL,來加載URL下的類,這種方式更加常見,如publicclassURLPathClassLoaderextendsURLClassLoader{publicclassURLPathClassLoaderextendsURLClassLoader{privateStringpackageName={}protectedClass<?>findClass(Stringname)throws{Class<?>aClass=findLoadedClass(name);if(aClass!=null){return}ifreturn}}}我們將指定的目錄轉(zhuǎn)化成URL路徑然后作為參數(shù)創(chuàng)建URLPathClassLoader對象,那個這個ClassLoader在加載的時候就是在URL指定的目錄下查找指定的類文件。假設(shè)我們通過網(wǎng)絡(luò)從遠(yuǎn)處主機(jī)上下載一個class文件的字節(jié)碼,但是我們?yōu)榱税踩裕趥鬏斨皩@個字節(jié)碼進(jìn)行了簡單的加密處理,然后在通過網(wǎng)絡(luò)傳輸。當(dāng)客戶端接收到這個類的字節(jié)碼后需要經(jīng)過解密才能還原成原始的類格式,然后在通過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中可以對網(wǎng)絡(luò)傳輸過來的字節(jié)碼進(jìn)行某種解密處理,然后返回正確的class字節(jié)碼調(diào)用defineClass來創(chuàng)建Class對象。我們知道JVM在加載類之前會檢查請求的類是否已經(jīng)被加載過來,也就是要調(diào)用findLoadedClass方法看是否能夠返回Class實例。如果類已經(jīng)加載過來在調(diào)用loadClass將會導(dǎo)致類沖突。但是JVM表示一個類是否是同一個類會有兩個條件。一個是看這個類的完整類名是否一樣,這個類名包括類所在的包名。還有一個是加載這個類的ClassLoader是否是同一個,這里所說的同一個是指ClassLoader的實例是否是同一個實例。如果是同一個ClassLoader類的兩個實例加載同一個類也會不一樣。所以要實現(xiàn)類的熱部署那么可以通過創(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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論