摘要:未加載返回已加載返回類對象系統(tǒng)計(jì)時器的當(dāng)前值納秒父根鏈接一個指定的類由自定義類加載器重載體現(xiàn)了以上所述的類加載邏輯。為自定義類加載器提供了入口。
這周在看《深入理解Java虛擬機(jī) JVM高級特性與最佳實(shí)踐(高清完整版)》,就地取材寫寫第7章中提到的類加載器。以下源碼截自java8。
delegation model*The ClassLoader class uses a delegation model to search for * classes and resources. Each instance of ClassLoader has an * associated parent class loader. When requested to find a class or * resource, a ClassLoader instance will delegate the search for the * class or resource to its parent class loader before attempting to find the * class or resource itself. The virtual machine"s built-in class loader, * called the "bootstrap class loader", does not itself have a parent but may * serve as the parent of a ClassLoader instance.
截取自源碼開篇注釋。“delegation model”大部分文章譯為“雙親委派模型”(個人感覺不是很貼切,“雙”字很容易產(chǎn)生誤解),闡述了一種類加載順序關(guān)系。請求查找類或資源時,ClassLoader實(shí)例會先交給父級類加載器處理(組合實(shí)現(xiàn),非繼承),依次類推直到"bootstrap class loader",父級無法處理(在其范圍內(nèi)找不到對應(yīng)類/資源)了再由自己加載。據(jù)說這樣可以避免同名類引發(fā)的安全隱患。類加載順序如下圖。
loadClass --> findClass/** * Loads the class with the specified binary name. * This method searches for classes in the same manner as the {@link * #loadClass(String, boolean)} method. It is invoked by the Java virtual * machine to resolve class references. Invoking this method is equivalent * to invoking {@link #loadClass(String, boolean) loadClass(name, * false)}. * * @param name * The binary name of the class * * @return The resulting Class object * * @throws ClassNotFoundException * If the class was not found */ public Class> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } /** * Loads the class with the specified binary name. The * default implementation of this method searches for classes in the * following order: * ** *
* *- * *
Invoke {@link #findLoadedClass(String)} to check if the class * has already been loaded.
- * *
Invoke the {@link #loadClass(String) loadClass} method * on the parent class loader. If the parent is null the class * loader built-in to the virtual machine is used, instead.
- * *
Invoke the {@link #findClass(String)} method to find the * class.
If the class was found using the above steps, and the * resolve flag is true, this method will then invoke the {@link * #resolveClass(Class)} method on the resulting Class object. * *
Subclasses of ClassLoader are encouraged to override {@link * #findClass(String)}, rather than this method.
* *Unless overridden, this method synchronizes on the result of * {@link #getClassLoadingLock getClassLoadingLock} method * during the entire class loading process. * * @param name * The binary name of the class * * @param resolve * If true then resolve the class * * @return The resulting Class object * * @throws ClassNotFoundException * If the class could not be found */ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded // VM未加載返回null;已加載返回類對象 Class> c = findLoadedClass(name); if (c == null) { // 系統(tǒng)計(jì)時器的當(dāng)前值(納秒) 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 // from the non-null parent class loader } 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 stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } // 鏈接一個指定的類 if (resolve) { resolveClass(c); } return c; } } /** * Finds the class with the specified binary name. * This method should be overridden by class loader implementations that * follow the delegation model for loading classes, and will be invoked by * the {@link #loadClass loadClass} method after checking the * parent class loader for the requested class. The default implementation * throws a ClassNotFoundException. * * @param name * The binary name of the class * * @return The resulting Class object * * @throws ClassNotFoundException * If the class could not be found * * @since 1.2 */ protected Class> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); // 由自定義類加載器重載 }
體現(xiàn)了以上所述的類加載邏輯。findClass為自定義類加載器提供了入口。
defineClass/** * Converts an array of bytes into an instance of class Class, * with an optional ProtectionDomain. If the domain is * null, then a default domain will be assigned to the class as * specified in the documentation for {@link #defineClass(String, byte[], * int, int)}. Before the class can be used it must be resolved. * *The first class defined in a package determines the exact set of * certificates that all subsequent classes defined in that package must * contain. The set of certificates for a class is obtained from the * {@link java.security.CodeSource CodeSource} within the * ProtectionDomain of the class. Any classes added to that * package must contain the same set of certificates or a * SecurityException will be thrown. Note that if * name is null, this check is not performed. * You should always pass in the binary name of the * class you are defining as well as the bytes. This ensures that the * class you are defining is indeed the class you think it is. * *
The specified name cannot begin with "java.", since * all classes in the "java.* packages can only be defined by the * bootstrap class loader. If name is not null, it * must be equal to the binary name of the class * specified by the byte array "b", otherwise a {@link * NoClassDefFoundError NoClassDefFoundError} will be thrown.
* * @param name * The expected binary name of the class, or * null if not known * * @param b * The bytes that make up the class data. The bytes in positions * off through off+len-1 should have the format * of a valid class file as defined by * The Java™ Virtual Machine Specification. * * @param off * The start offset in b of the class data * * @param len * The length of the class data * * @param protectionDomain * The ProtectionDomain of the class * * @return The Class object created from the data, * and optional ProtectionDomain. * * @throws ClassFormatError * If the data did not contain a valid class * * @throws NoClassDefFoundError * If name is not equal to the binary * name of the class specified by b * * @throws IndexOutOfBoundsException * If either off or len is negative, or if * off+len is greater than b.length. * * @throws SecurityException * If an attempt is made to add this class to a package that * contains classes that were signed by a different set of * certificates than this class, or if name begins with * "java.". */ protected final Class> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class> c = defineClass1(name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain); return c; }
defineClass:接收以字節(jié)數(shù)組表示的類字節(jié)碼,并把它轉(zhuǎn)換成 Class 實(shí)例,該方法轉(zhuǎn)換一個類的同時,會先要求裝載該類的父類以及實(shí)現(xiàn)的接口類。重寫findClass將使用到。
public class Test { private static String link = "rebey.cn"; static { System.out.println("welcome to: "+link); } public void print() { System.out.println(this.getClass().getClassLoader()); } }
將這段java代碼編譯成.class文件(可通過javac指令),放在 了E:201706下。同時在我的測試項(xiàng)目下也有一個/201705/src/classLoader/Test.java,代碼相同。區(qū)別就是一個有包名一個沒有包名。如果class文件中源碼包含package信息,屆時可能會拋出java.lang.NoClassDefFoundError (wrong name)異常。
package classLoader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; public class CustomClassLoader extends ClassLoader{ private String basedir; // 需要該類加載器直接加載的類文件的基目錄 public CustomClassLoader(String basedir) { super(null); this.basedir = basedir; } protected Class> findClass(String name) throws ClassNotFoundException { Class> c = findLoadedClass(name); if (c == null) { byte[] bytes = loadClassData(name); if (bytes == null) { throw new ClassNotFoundException(name); } c = defineClass(name, bytes, 0, bytes.length); } return c; } // 摘自網(wǎng)絡(luò) public byte[] loadClassData(String name) { try { name = name.replace(".", "http://"); FileInputStream is = new FileInputStream(new File(basedir + name + ".class")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int b = 0; while ((b = is.read()) != -1) { baos.write(b); } is.close(); return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } }
編寫自定義的類加載器,繼承ClassLoader,重寫了findClass方法,通過defineClass將讀取的byte[]轉(zhuǎn)為Class。然后通過以下main函數(shù)調(diào)用測試:
package classLoader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Loader { public static void main(String[] arg) throws NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ // 走自定義加載器 CustomClassLoader ccl = new CustomClassLoader("E://201706//"); Class> clazz = ccl.findClass("Test"); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("print", null); method.invoke(obj, null); System.out.println("--------------我是分割線-------------------"); // 走委派模式 // 隱式類加載 Test t1 = new Test(); t1.print(); System.out.println("--------------我是分割線-------------------"); // 顯式類加載 Class> t2 = Class.forName("classLoader.Test"); Object obj2 = t2.newInstance(); Method method2 = t2.getDeclaredMethod("print", null); method2.invoke(obj2, null); System.out.println("--------------我是分割線-------------------"); Classt3 = Test.class; Object obj3 = t3.newInstance(); Method method3 = t3.getDeclaredMethod("print", null); method3.invoke(obj3, null); } } 輸出結(jié)果: welcome to: rebey.cn classLoader.CustomClassLoader@6d06d69c --------------我是分割線------------------- welcome to: rebey.cn sun.misc.Launcher$AppClassLoader@73d16e93 --------------我是分割線------------------- sun.misc.Launcher$AppClassLoader@73d16e93 --------------我是分割線------------------- sun.misc.Launcher$AppClassLoader@73d16e93
靜態(tài)代碼塊隨著類加載而執(zhí)行,而且只會執(zhí)行一次,所以這里t2、t3加載完成是并沒有再輸出。
說點(diǎn)什么ClassLoader線程安全;
同個類加載器加載的.class類實(shí)例才相等;
Class.forName(xxx.xx.xx) 返回的是一個類, .newInstance() 后才創(chuàng)建實(shí)例對象 ;
Java.lang.Class對象是單實(shí)例的;
執(zhí)行順序:靜態(tài)代碼塊 > 構(gòu)造代碼塊 > 構(gòu)造函數(shù)
應(yīng)用1、父類靜態(tài)變量和靜態(tài)代碼塊(先聲明的先執(zhí)行);
2、子類靜態(tài)變量和靜態(tài)代碼塊(先聲明的先執(zhí)行);
3、父類的變量和代碼塊(先聲明的先執(zhí)行);
4、父類的構(gòu)造函數(shù);
5、子類的變量和代碼塊(先聲明的先執(zhí)行);
6、子類的構(gòu)造函數(shù)。
通過自定義加載類,我們可以:
①加載指定路徑的class,甚至是來自網(wǎng)絡(luò)(自定義類加載器:從網(wǎng)上加載class到內(nèi)存、實(shí)例化調(diào)用其中的方法)、DB(自定義的類裝載器-從DB裝載class(附上對類裝載器的分析));
②給代碼加密;(如何有效防止Java程序源碼被人偷窺?)
③裝逼(- -);
更多有意思的內(nèi)容,歡迎訪問筆者小站: rebey.cn
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/67249.html
摘要:自定義類加載器示例代碼類加載器獲取的字節(jié)流字節(jié)流解密被加載的類測試代碼以上代碼,展示了自定義類加載器加載類的方法。這就需要自定義類加載器,以便對加載的類庫進(jìn)行隔離,否則會出現(xiàn)問題對于非的文件,需要轉(zhuǎn)為類,就需要自定義類加載器。 Java類加載器的作用是尋找類文件,然后加載Class字節(jié)碼到JVM內(nèi)存中,鏈接(驗(yàn)證、準(zhǔn)備、解析)并初始化,最終形成可以被虛擬機(jī)直接使用的Java類型。sho...
摘要:當(dāng)前類加載器和所有父類加載器都無法加載該類時,拋出異常。加載兩份相同的對象的情況和不屬于父子類加載器關(guān)系,并且各自都加載了同一個類。類加載機(jī)制與接口當(dāng)虛擬機(jī)初始化一個類時,不會初始化該類實(shí)現(xiàn)的接口。 類加載機(jī)制 概念 類加載器把class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,存放在方法區(qū),然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。 1、加載: 查...
摘要:宗主引導(dǎo)類加載器。雙親委派模型是如何使用的我們在自定義加載器中查找是否有需要加載的文件,如果已經(jīng)加載過,直接返回字節(jié)碼。 作者:畢來生微信:878799579 1、小故事理解類加載器以及雙親委派模型 首先我們來描述一個小說場景,通過這個場景在去理解我們相關(guān)的類加載器的執(zhí)行以及雙親委派模型。 上古時代有逍遙派和萬魔宗兩個宗派,互相對立。逍遙派比萬魔門更加強(qiáng)勢。巔峰戰(zhàn)力更高。 有一天萬魔宗...
摘要:當(dāng)一個文件是通過網(wǎng)絡(luò)傳輸并且可能會進(jìn)行相應(yīng)的加密操作時,需要先對文件進(jìn)行相應(yīng)的解密后再加載到內(nèi)存中,這種情況下也需要編寫自定義的并實(shí)現(xiàn)相應(yīng)的邏輯 Java虛擬機(jī)中的類加載有三大步驟:,鏈接,初始化.其中加載是指查找字節(jié)流(也就是由Java編譯器生成的class文件)并據(jù)此創(chuàng)建類的過程,這中間我們需要借助類加載器來查找字節(jié)流. Java虛擬機(jī)默認(rèn)類加載器 Java虛擬機(jī)提供了3種類加載器...
摘要:類加載過程雙親委派模型聲明文章均為本人技術(shù)筆記,轉(zhuǎn)載請注明出處類加載過程類加載機(jī)制將類描述數(shù)據(jù)從文件中加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行,解析和初始化,最終形成被直接使用的類型。深入理解虛擬機(jī)高級特性與最佳實(shí)踐加載加載階段由類加載器負(fù)責(zé),過程見類加載 JVM類加載過程 & 雙親委派模型 聲明 文章均為本人技術(shù)筆記,轉(zhuǎn)載請注明出處https://segmentfault.com/u/yzwall ...
摘要:在沒有指定自定義類加載器的情況下,這就是程序的默認(rèn)加載器。自定義類加載器雙親委派模型避免由于字節(jié)碼被多次加載。首先自定義類加載器,最重要的就是先繼承這個類。 一. 類加載器 JVM中的類加載器:在jvm中,存在兩種類加載器,a) Boostrap ClassLoader:這個是由c++實(shí)現(xiàn)的,所以在方法區(qū)并沒有Class對象的實(shí)例存在。用于加載JAVA_HOME/bin目錄...
閱讀 4002·2023-04-26 02:13
閱讀 2244·2021-11-08 13:13
閱讀 2729·2021-10-11 10:59
閱讀 1732·2021-09-03 00:23
閱讀 1301·2019-08-30 15:53
閱讀 2275·2019-08-28 18:22
閱讀 3050·2019-08-26 10:45
閱讀 727·2019-08-23 17:58