【JVM類加載】第五天:系統自帶的類加載器如何加載如何自定義類加載器和 getSystemClassLoader()延伸的源碼分析和分析Class.forName源碼分析

系統自帶的類加載器如何加載

  • 內建於JVM中的啓動類加載器會加載java.lang.classLoader以及其他平臺的Java平臺類,當JVM啓動時,一塊特殊的機器碼會運行,他會加載擴展類加載器與系統類加載器,
    這快特殊的機器碼叫做啓動類加載器(Bootstrap)
  • 啓動類加載器並不是java類(C++編寫),而其他的加載器則都是Java類,啓動類加載器是特定於平臺的機器指令,它負責開啓整個加載過程
  • 所有類加載器(除了啓動類加載器)都被實現爲java類。不過,總歸要有一個組件來加載第一個Java類加載器,從而讓整個加載過程能夠順利進行下去,加載第一個純java類加載器就是啓動類的職責
  • 啓動類加載器還會負責加載供JRE正常運行所需要的基本組件,這包括java.util與java.lang包中的類等等

自定義系統類加載器

/*
    在運行期,一個java類是由該類的完全限定名(binary name,二進制名) 和用於加載該類的定義類加載器(defining loader)所共同決定的。
    如果同樣名字(即相同的完全限定名)的類是由兩個不同的加載器所加載,那麼這些類就是不同的,即便.class文件的字節碼完全一樣,並且從
    相同的位置加載亦如此。
 */

/*
    在Oracle的Hotspot實現中,系統屬性sun.boot.class.path如果修改錯了,則運行會出差,提示如下錯誤信息:

    Error occurred during initialization of VM
    java/lang/NOClassDefFoundError: java/lang/Object
 */

public class MyTest23 {
    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));//根類加載器
        System.out.println(System.getProperty("java.ext.dirs"));//擴展類加載器
        System.out.println(System.getProperty("java.class.path"));//應用類加載器  當在編譯之後的.class文件會被放到classes下 所以會被該類加載器加載

        /*

            內建於JVM中的啓動類加載器,會加載java.lang.classLoader以及其他平臺的Java平臺類,
            當JVM啓動時,一塊特殊的機器碼會運行,他會加載擴展類加載器與系統類加載器,
            這快特殊的機器碼焦作啓動類加載器(Bootstrap).

            啓動類加載器並不是java類,而其他的加載器則都是Java類,
            啓動類加載器是特定於平臺的機器指令,它負責開啓整個加載過程。

            所有類加載器(除了啓動類加載器)都被實現爲java類。不過,總歸要有一個組件來加載第一個Java類加載器,從而讓整個加載過程能夠順利進行下去,加載第一個純java類加載器就是啓動類
            的職責。

            啓動類加載器還會負責加載供JRE正常運行所需要的基本組件,這包括java.util與java.lang包中的類等等

         */

        System.out.println(ClassLoader.class.getClassLoader());//null 內建於JVM中的啓動類加載器,會加載java.lang.classLoader以及其他平臺的Java平臺類,

        //擴展類加載器與系統類加載器也是由啓動類加載器加載的
        System.out.println(Launcher.class.getClassLoader());//null 因爲APPClassLoader和ExtClassLoader都是該類的靜態內部類 二類加載器加載時其加載類的其他內容都是由該類加載器加載

        System.out.println(System.getProperty("java.system.class.loader"));//文件指定路徑

        System.out.println(MyTest23.class.getClassLoader());

        System.out.println(Test16.class.getClassLoader());
        
		System.out.println(ClassLoader.getSystemClassLoader());//etSystemClassLoader靜態方法 類名.獲取系統類加載器
    }
}

在ClassLoader的doc文檔中

  * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
     * when this method is first invoked then the value of that property is
     * taken to be the name of a class that will be returned as the system
     * class loader.  The class is loaded using the default system class loader
     * and must define a public constructor that takes a single parameter of
     * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
     * instance is then created using this constructor with the default system
     * class loader as the parameter.  The resulting class loader is defined
     * to be the system class loader.

如果系統屬性java.system.class.loader”定義首次調用此方法時(如果java.system.class.loader指定的路徑爲null未定義時 會去使用AppClassLoader作爲系統類加載器,當定義了系統屬性時系統會讓默認的AppClassLoader這個類加載器去加載即將成爲系統類加載這個自定義類加載器),該屬性的值(方法名)將作爲系統類加載器的名字。類會使用默認的系統類加載器裝入的必須定義一個公共構造函數該構造函數只接受一個參數類型類加載器,用作委託父類(ClassLoader的一個成員變量每個子類都有)然後使用默認系統類加載器作爲這個構造函數參數。此時自定義的類加載器將成爲系統類裝入器。

當我們直接run時

null
null
null 此時java.system.class.loader 並沒有指定自定義路徑下的類爲系統加載類
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

根據文檔修改默認的系統類加載器時必須在自定義的類加載器中編寫一個構造函數該構造函數只接受一個參數類型類裝入器,用作委託父類。在Test16中加入對應的構造函數 重新編譯

//新加一個構造方法
    public Test16(ClassLoader parent) {
        super(parent);
    }

在控制檯輸入 將java.system.class.loader 指定的類加載器設置爲com.example.demo.com.jvm.Test16 並加載com.example.demo.com.jvm.MyTest23

java -Djava.system.class.loader=com.example.demo.com.jvm.Test16 com.example.demo.com.jvm.MyTest23

null
null
com.example.demo.com.jvm.Test16
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2 //此時AppClassLoader是自定義系統類加載器的父類 父類可以加載 就直接輸出
com.example.demo.com.jvm.Test16@7852e922 //此時系統類加載器爲我們自定義的類加載器

getSystemClassLoader()源碼分析

問題1,爲什麼自帶的類加載器加載指定位置?
問題1,爲什麼系統類加載器是擴展類加載器的子類?
doc文檔

 Returns the system class loader for delegation.  This is the default
     * delegation parent for new <tt>ClassLoader</tt> instances, and is
     * typically the class loader used to start the application.
     *
     * <p> This method is first invoked early in the runtime's startup
     * sequence, at which point it creates the system class loader and sets it
     * as the context class loader of the invoking <tt>Thread</tt>.
     *
     * <p> The default system class loader is an implementation-dependent
     * instance of this class.
     *
     * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
     * when this method is first invoked then the value of that property is
     * taken to be the name of a class that will be returned as the system
     * class loader.  The class is loaded using the default system class loader
     * and must define a public constructor that takes a single parameter of
     * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
     * instance is then created using this constructor with the default system
     * class loader as the parameter.  The resulting class loader is defined
     * to be the system class loader.
     *
  
  • 返回用於委託的系統類裝入器。他是新的(自定義)類加載器的默認委託雙親(父類),並且是通常用於啓動應用程序的類加載器。(main方法)
  • 此方法首先在程序運行時啓動時很早被調用,此時創建並設置系統類加載器;並設置爲:將調用該方法線程的上下文類加載器。
  • 默認的系統類加載器是依賴於實現的這個類的實例。
  • 如果系統屬性java.system.class.loader”定義首次調用此方法時(如果java.system.class.loader指定的路徑爲null未定義
    會去使用AppClassLoader作爲系統類加載器,當定義了系統屬性時系統會讓默認的AppClassLoader這個類加載器去加載即將成爲系統類加載這個自定義類加載器),該屬性的值(方法名)將作爲系統類加載器的名字。類會使用默認的系統類加載器裝入的必須定義一個公共構造函數該構造函數只接受一個參數類型類加載器,用作委託父類(ClassLoader的一個成員變量每個子類都有)然後使用默認系統類加載器作爲這個構造函數參數。此時自定義的類加載器將成爲系統類裝入器。

ClassLoader類中

  //獲取系統類加載器
  @CallerSensitive
    public static ClassLoader getSystemClassLoader() {
    	//初始化系統類加載器
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        //獲取安全管理器
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
   //初始化系統類加載器
   private static synchronized void initSystemClassLoader() {
   		//如果系統類加載器沒有被設置
        if (!sclSet) {
        	//系統類加載器沒有沒有被設置又不爲空 矛盾 就拋異常
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
             //Launcher.getLauncher 獲取該實例(解析在下)
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
            	//創建成功
                Throwable oops = null;
                //把Launcher 中的系統類加載器付給當前類的類加載器
                scl = l.getClassLoader();
                try {
                //獲取類加載器對象(有可能是appClassLoader或者自定義類加載器)
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));//傳入系統類加載器作爲父加載器
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }
                if (oops != null) {
                    if (oops instanceof Error) {
                        throw (Error) oops;
                    } else {
                        // wrap the exception
                        throw new Error(oops);
                    }
                }
            }
            //此時系統類加載器設置完成
            sclSet = true;
        }
    }

參數

	//委託雙親 
   private final ClassLoader parent;
   // 系統類加載器
    // @GuardedBy("ClassLoader.class")
    private static ClassLoader scl;

    // 如果設置了系統類加載器的話 sclSet 爲true
    // @GuardedBy("ClassLoader.class")
    private static boolean sclSet;

SystemClassLoaderAction類分析

class SystemClassLoaderAction
    implements PrivilegedExceptionAction<ClassLoader> {
    private ClassLoader parent;

    SystemClassLoaderAction(ClassLoader parent) {
        this.parent = parent;
    }

    public ClassLoader run() throws Exception {
    //獲取java.system.class.loader 系統屬性
        String cls = System.getProperty("java.system.class.loader");
        if (cls == null) {
        //如果改系統屬性沒被設置返回 appClassLoader
            return parent;
        }
		//如果自定義了系統類加載器
		//獲取cls名的Class參數爲(ClassLoader.class)的構造函數 
		//Class.forName 加載cls對應的二進制名 並使用父類(parent)初始化(true)
        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        //將父類加載器即系統類加載器傳給了自定義類加載器
        //所以當自定義類加載器在設置完時是由appClassLoader去加載的
        //獲取自定義的ClassLoader類
        ClassLoader sys = (ClassLoader) ctor.newInstance(
        //爲什麼自定義類加載器必須要編寫一個接收ClassLoader構造方法定義個父類參數的原因
            new Object[] { parent });
            
        Thread.currentThread().setContextClassLoader(sys);
        //返回自定義類加載器
        return sys;
    }
}

Launcher

getLauncher內容講解

在這裏插入圖片描述
進入Launcher的無參構造方法
上下文類加載器

 public Launcher() {
 		//創建擴展類加載器
        Launcher.ExtClassLoader var1;//定義個擴展類加載器
        try {
            //創建擴展類加載器實例
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
        	//創建應用類加載器實例,並將擴展類加載器作爲父類 並將其作爲Launcher的一個成員變量
        	//爲什麼不把擴展類加載器也作爲成員變量 沒必要 因爲 應用類加載器作爲其子類可以獲取到
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
		//將系統類加載器設置爲當前的執行線程設置一個上下文類加載器(*重要*)
        Thread.currentThread().setContextClassLoader(this.loader);
        //下面安全管理器內容
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                    ;
                } catch (InstantiationException var6) {
                    ;
                } catch (ClassNotFoundException var7) {
                    ;
                } catch (ClassCastException var8) {
                    ;
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

getExtClassLoader() 獲取已經加載的擴展類加載器

 static class ExtClassLoader extends URLClassLoader {
 //創建的擴展類加載器限制java.ext.dirs地區class文件
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
        	//獲取指定文件數組  System.getProperty("java.ext.dirs");
            final File[] var0 = getExtDirs();

            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(
                //返回時判斷是否有權限去執行該操作
                new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        //獲取路徑長度
                        int var1 = var0.length;
						//遍歷每一個路徑獲取對應路徑內容
                        for(int var2 = 0; var2 < var1; ++var2) {
                            MetaIndex.registerDirectory(var0[var2]);
                        }
						//返回擴展類對象給調用端
                        return new Launcher.ExtClassLoader(var0);
                    }
                });
            } catch (PrivilegedActionException var2) {
                throw (IOException)var2.getException();
            }
        }

在這裏插入圖片描述從java.class.path下加載指定內容
getAppClassLoader(final ClassLoader var0) 獲取系統類加載器
爲什麼擴展類加載器是系統類加載器父類? 解答如下

  static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        	//讀取指定路徑下的文件
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                   // 生成系統類加載器 傳對應的數據和擴展類加載器
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        AppClassLoader(URL[] var1, ClassLoader var2) {
        	//調用父類完成雙親委託 將擴展類加載器作爲父類
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

分析Class.forName

public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)

  • 返回給定名字(String name)的類或接口的Class對象。用給定的( ClassLoader loader)的類加載器去加載這個方法試圖定位、加載和鏈接類或接口。指定的類加載器用於加載類或接口。如果參數loader爲null,類通過bootstrap類加載器加載。這個類類只有在initialize參數是true,尚未被初始化的情況下才會被初始化
  • 如果{String name}表示原始類型或void,則嘗試將被用來在其未命名的包中尋找一個用戶定義的類 name是{String name}。因此,這個方法不能用於獲取表示原生類型或void的任何 Class對象。
  • 如果{String name}表示一個數組類,則組件類型(jvm)爲加載了數組類,但沒有初始化。
  • 例如,在一個實例方法中的表達式: Class.forName (" Foo ")

Class<?> caller = Reflection.getCallerClass();獲取調用者的類加載器

//該會將調用者的類加載器去加載className的類
 public static Class<?> forName(String className)
                throws ClassNotFoundException {
        //獲取調用者的Class對象 單參默認使用調用者的類加載器
        Class<?> caller = Reflection.getCallerClass();
        //返回Class 使用C++實現
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

相當於:
Class.forName(“foo”,true, this.getClass.getClassLoader()) 這個方法是使用 自定義的類加載器

無指定加載器使用當前加載這個類的加載器去加載這個類,如果指定了類加載器就會使用指定的類加載器去加載

  • 注意此方法拋出與加載、鏈接或初始化相關的錯誤按照的第12.2、12.3和12.4節的規定進行初始化Java語言規範。注意,這個方法不會檢查被請求的類是否存在調用者可以訪問*。
    String name:指定類完整的限定名
    boolean initialize:是否初始化
    ClassLoader loader:用於去加載這個類的類加載器
    源代碼
  public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            //獲取調用forname方法的類的Class對象
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
            	//獲取調用forname方法的類的Class對象的類加載器
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                	//安全檢查
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        //c++代碼
        return forName0(name, initialize, loader, caller);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章