jvm原理(20)Launcher類源碼分析以及forName方法底層剖析

上一節我們通過【ClassLoader.getSystemClassLoader()】得到系統類加載器,那麼本節看一下這個方法的doc以及一些細節,方便我們更好的理解:
public static ClassLoader getSystemClassLoader()
Returns the system class loader for delegation. This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application.
返回用於代理的系統類加載器,對於一個類加載器實例來說這是默認的父級代理,也是最典型的用於啓動應用的類加載器。
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 Thread.
這個方法最早在運行時啓動的就會被調用,在這個調用點上回去創建胸膛類加載器,並且將其設置在調用線程爲【上下文加載器】。
The default system class loader is an implementation-dependent instance of this class.
默認的系統類加載器是一個與這個類實現相關的實例
If the system property “java.system.class.loader” 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 ClassLoader 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】被定義,那麼這個屬性的值對應的class會被默認的系統類加載器 加載,並且作爲系統類加載器,這個被定義的加載器必須有一個單個參數的構造器,參數的類型是ClassLoader,用來代理父加載器,使用這個構造器傳入默認的的系統類加載器會創建一個類加載器的實例,這個實例會成爲系統類加載器。
If a security manager is present, and the invoker’s class loader is not null and the invoker’s class loader is not the same as or an ancestor of the system class loader, then this method invokes the security manager’s checkPermission method with a RuntimePermission(“getClassLoader”) permission to verify access to the system class loader. If not, a SecurityException will be thrown.
安全略過,主要是介紹一些安全接入的權限是否可以接入

伏筆:
1、【上下文加載器】
2、定義ClassLoader類型的構造器
方法體:

    public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();//初始化系統類加載器
        if (scl == null) {
            return null;
        }
        //下邊的是安全相關的,不做介紹
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }

進入到initSystemClassLoader():

PS:
sclSet、scl 成員變量介紹:
    // The class loader for the system
    // @GuardedBy("ClassLoader.class")
    系統類加載器在ClassLoader的引用
    private static ClassLoader scl;

    // Set to true once the system class loader has been set
    // @GuardedBy("ClassLoader.class")
    //如果系統類加載器被加載過,即scl成員變量不是null,那麼sclSet的值爲true
    private static boolean sclSet;

    private static synchronized void initSystemClassLoader() {
        //系統類加載器是否被設置
        if (!sclSet) {
             //雙重判斷,即sclSet是true,但是系統類加載器是null,排除非法異常
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
                //通過sun.misc.Launcher獲取系統類加載器。sun.misc.Launcher是系統類加載器,擴展類加載器上層的一個概念,並且Oracle不是開源的
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                scl = l.getClassLoader();
                try {
                    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;
        }
    }

我們在idea裏邊看到的sun.misc.Launcher.getLauncher()的實現是反編譯工具給出的,oracle並沒有給出源碼,但是我們可以去openjdk獲取他的源碼,也可以使用http://grepcode.com/search查看源碼:
http://grepcode.com/search/?query=sun.misc.launcher
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/misc/Launcher.java#Launcher
sun.misc.Launcher.getLauncher()z返回值是sun.misc.Launcher內部的一個靜態成員變量【 private static Launcher launcher = new Launcher();】
那麼我們看一下Launcher的構造器:

public Launcher() {
68         // Create the extension class loader
            //獲得系統類加載器
69         ClassLoader extcl;
70         try {
                //ExtClassLoader是Launcher的一個內部類,getExtClassLoader()getExtClassLoader()獲取擴展類加載器,
                /**
                    getExtClassLoader()方法裏邊返回擴展類加載器實例,並且通過系統屬性java.ext.dirs加載其需要加載的類
                */
71             extcl = ExtClassLoader.getExtClassLoader();
72         } catch (IOException e) {
73             throw new InternalError(
74                 "Could not create extension class loader", e);
75         }
76 
77         // Now create the class loader to use to launch the application
            //獲取系統類加載器,並且將擴展類加載器作爲他的委託傳入
78         try {
79             loader = AppClassLoader.getAppClassLoader(extcl);
80         } catch (IOException e) {
81             throw new InternalError(
82                 "Could not create application class loader", e);
83         }
84 
85         // Also set the context class loader for the primordial thread.
            //將系統類加載器作爲當前線程上下文的加載器
86         Thread.currentThread().setContextClassLoader(loader);
87 
88         // Finally, install a security manager if requested
89         String s = System.getProperty("java.security.manager");
90         if (s != null) {
91             SecurityManager sm = null;
92             if ("".equals(s) || "default".equals(s)) {
93                 sm = new java.lang.SecurityManager();
94             } else {
95                 try {
96                     sm = (SecurityManager)loader.loadClass(s).newInstance();
97                 } catch (IllegalAccessException e) {
98                 } catch (InstantiationException e) {
99                 } catch (ClassNotFoundException e) {
100                } catch (ClassCastException e) {
101                }
102            }
103            if (sm != null) {
104                System.setSecurityManager(sm);
105            } else {
106                throw new InternalError(
107                    "Could not create SecurityManager: " + s);
108            }
109        }
110    }

擴展類加載器獲取加載文件數組:

169        private static File[]  getExtDirs() {
                //加載來源來自於系統屬性java.ext.dirs
170            String s = System.getProperty("java.ext.dirs");
171            File[] dirs;
172            if (s != null) {
173                StringTokenizer st =
174                    new StringTokenizer(s, File.pathSeparator);
175                int count = st.countTokens();
176                dirs = new File[count];
177                for (int i = 0; i < count; i++) {
178                    dirs[i] = new File(st.nextToken());
179                }
180            } else {
181                dirs = new File[0];
182            }
183            return dirs;
184        }

getAppClassLoader的方法:
入參是擴展類加載器

267        public static ClassLoader getAppClassLoader(final ClassLoader extcl)
268            throws IOException
269        {
                //通過系統屬性java.class.path獲取加載路徑
270            final String s = System.getProperty("java.class.path");
271            final File[] path = (s == null) ? new File[0] : getClassPath(s);
272
273            // Note: on bugid 4256530
274            // Prior implementations of this doPrivileged() block supplied
275            // a rather restrictive ACC via a call to the private method
276            // AppClassLoader.getContext(). This proved overly restrictive
277            // when loading  classes. Specifically it prevent
278            // accessClassInPackage.sun.* grants from being honored.
279            //
280            return AccessController.doPrivileged(
281                new PrivilegedAction<AppClassLoader>() {
282                    public AppClassLoader More ...run() {
283                    URL[] urls =
284                        (s == null) ? new URL[0] : pathToURLs(path);
                        //構造系統類加載器的時候將他的委託父類傳入,以及加載路徑數組
285                    return new AppClassLoader(urls, extcl);
286                }
287            });
288        }
289
290        final URLClassPath ucp;
291
292        /*
293         * Creates a new AppClassLoader
294         */
295        AppClassLoader(URL[] urls, ClassLoader parent) {
                //調用父類URLClassLoader,注意不管是系統類加載器還是擴展類加載器都是繼承來自ClassLoader
                //CLassLoader裏邊之前說過有一個硬編碼的成員變量【private final ClassLoader parent;】
                //此處的parent一直溯源到ClassLoader 類,將ClassLoader 的parent賦值爲當前方法傳入的parent入參,
                //也就說明了系統類加載器的委託父類就是擴展類加載器
296            super(urls, parent, factory);
297            ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
298            ucp.initLookupCache(this);
299        }

然後我們再回過頭來看看idea裏邊反編譯出來的獲取系統類加載器的方法:
這裏寫圖片描述
大家可能會有疑問,爲什麼反編譯出來的變量都是var1、var2之類的呢?其實這個在龍哥的kotlin課程中【27_Kotlin函數使用綜述與顯式返回類型分析】一講裏邊介紹過,Java中並不總是在class 字節碼中攜帶變量名,但是在kotlin裏邊就不是這樣的,因爲kotlin裏邊有具名參數的概念。

ok,我們回到initSystemClassLoader()方法,通過調用【 sun.misc.Launcher l = sun.misc.Launcher.getLauncher();】獲取到系統類加載器之後,賦值給ClassLoader的scl成員變量之後,還有一塊代碼:

                try {
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }

這塊邏輯的作用是看看是否設置了系統屬性【java.system.class.loader】,即自定義的系統類加載器,如果設置了那麼實例化自定義的系統類加載器返回,替代之前獲取的系統類加載器,如果沒有設置直接返回默認的系統類加載器。

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

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

    public ClassLoader run() throws Exception {
        String cls = System.getProperty("java.system.class.loader");
        //自定義系統類加載器未定義,直接返回之前默認獲取的系統類加載器
        if (cls == null) {
            return parent;
        }
        //如果定義了自定義的系統類加載器那麼實例化並返回,替代默認的系統類加載器
        //etDeclaredConstructor(new Class<?>[] { ClassLoader.class })這個就是我們之前
        //(https://blog.csdn.net/wzq6578702/article/details/79835695)
        //在MyTest16裏邊加入一個參數是ClassLoader的構造器的原因,
        //因爲自定義的的加載器在這裏會被反射調用,
        //
        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        ClassLoader sys = (ClassLoader) ctor.newInstance(
            new Object[] { parent });
            //同樣的道理 將自定義系統類加載器作爲當前線程的上下文加載器。
        Thread.currentThread().setContextClassLoader(sys);
        return sys;
    }
}

那麼接下來介紹一下 Class.forName(cls, true, parent),Class.forName方法大部分做web開發的都是在開始寫jdbc的時候接觸,那麼先看一下doc:
java.lang.Class
public static Class

    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對象A
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                //獲取A的類加載器
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                //安全檢查
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        //name:要被加載的|initialize:是否初始化|loader:指定的類加載器(自定義加載器此處是系統類加載器)|caller調用者的Class對象
        return forName0(name, initialize, loader, caller);
    }
    //forName0是一個本地方法
    /** Called after security check for system loader access checks have been made. */
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

另外和只有一個參數的forName的區別:

   public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        // className:要加載的Class名字 | 是否初始化:此處需要初始化 |調用者的類加載器 | 調用者的Class對象
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章