这次重新阅读了一下sun.misc.Launcher这个类的源码,这里总结一下。

  1package sun.misc;
  2
  3import java.io.File;
  4import java.io.IOException;
  5import java.io.FilePermission;
  6import java.net.URL;
  7import java.net.URLClassLoader;
  8import java.net.MalformedURLException;
  9import java.net.URLStreamHandler;
 10import java.net.URLStreamHandlerFactory;
 11import java.util.HashSet;
 12import java.util.StringTokenizer;
 13import java.util.Set;
 14import java.util.Vector;
 15import java.security.AccessController;
 16import java.security.PrivilegedAction;
 17import java.security.PrivilegedExceptionAction;
 18import java.security.AccessControlContext;
 19import java.security.PermissionCollection;
 20import java.security.Permissions;
 21import java.security.Permission;
 22import java.security.ProtectionDomain;
 23import java.security.CodeSource;
 24import sun.security.util.SecurityConstants;
 25import sun.net.www.ParseUtil;
 26
 27/**
 28 * This class is used by the system to launch the main application.
 29Launcher */
 30public class Launcher {
 31    private static URLStreamHandlerFactory factory = new Factory();
 32    private static Launcher launcher = new Launcher();
 33    private static String bootClassPath =
 34        System.getProperty("sun.boot.class.path");
 35
 36    public static Launcher getLauncher() {
 37        return launcher;
 38    }
 39
 40    private ClassLoader loader;
 41
 42    public Launcher() {
 43        // Create the extension class loader
 44        ClassLoader extcl;
 45        try {
 46            extcl = ExtClassLoader.getExtClassLoader();
 47        } catch (IOException e) {
 48            throw new InternalError(
 49                "Could not create extension class loader", e);
 50        }
 51
 52        // Now create the class loader to use to launch the application
 53        try {
 54            loader = AppClassLoader.getAppClassLoader(extcl);
 55        } catch (IOException e) {
 56            throw new InternalError(
 57                "Could not create application class loader", e);
 58        }
 59
 60        // Also set the context class loader for the primordial thread.
 61        Thread.currentThread().setContextClassLoader(loader);
 62
 63        // Finally, install a security manager if requested
 64        String s = System.getProperty("java.security.manager");
 65        if (s != null) {
 66            SecurityManager sm = null;
 67            if ("".equals(s) || "default".equals(s)) {
 68                sm = new java.lang.SecurityManager();
 69            } else {
 70                try {
 71                    sm = (SecurityManager)loader.loadClass(s).newInstance();
 72                } catch (IllegalAccessException e) {
 73                } catch (InstantiationException e) {
 74                } catch (ClassNotFoundException e) {
 75                } catch (ClassCastException e) {
 76                }
 77            }
 78            if (sm != null) {
 79                System.setSecurityManager(sm);
 80            } else {
 81                throw new InternalError(
 82                    "Could not create SecurityManager: " + s);
 83            }
 84        }
 85    }
 86
 87    /*
 88     * Returns the class loader used to launch the main application.
 89     */
 90    public ClassLoader getClassLoader() {
 91        return loader;
 92    }
 93
 94    /*
 95     * The class loader used for loading installed extensions.
 96     */
 97    static class ExtClassLoader extends URLClassLoader {
 98
 99        static {
100            ClassLoader.registerAsParallelCapable();
101        }
102
103        /**
104         * create an ExtClassLoader. The ExtClassLoader is created
105         * within a context that limits which files it can read
106         */
107        public static ExtClassLoader getExtClassLoader() throws IOException
108        {
109            final File[] dirs = getExtDirs();
110
111            try {
112                // Prior implementations of this doPrivileged() block supplied
113                // aa synthesized ACC via a call to the private method
114                // ExtClassLoader.getContext().
115
116                return AccessController.doPrivileged(
117                    new PrivilegedExceptionAction<ExtClassLoader>() {
118                        public ExtClassLoader run() throws IOException {
119                            int len = dirs.length;
120                            for (int i = 0; i < len; i++) {
121                                MetaIndex.registerDirectory(dirs[i]);
122                            }
123                            return new ExtClassLoader(dirs);
124                        }
125                    });
126            } catch (java.security.PrivilegedActionException e) {
127                throw (IOException) e.getException();
128            }
129        }
130
131        void addExtURL(URL url) {
132            super.addURL(url);
133        }
134
135        /*
136         * Creates a new ExtClassLoader for the specified directories.
137         */
138        public ExtClassLoader(File[] dirs) throws IOException {
139            super(getExtURLs(dirs), null, factory);
140            SharedSecrets.getJavaNetAccess().
141                getURLClassPath(this).initLookupCache(this);
142        }
143
144        private static File[] getExtDirs() {
145            String s = System.getProperty("java.ext.dirs");
146            File[] dirs;
147            if (s != null) {
148                StringTokenizer st =
149                    new StringTokenizer(s, File.pathSeparator);
150                int count = st.countTokens();
151                dirs = new File[count];
152                for (int i = 0; i < count; i++) {
153                    dirs[i] = new File(st.nextToken());
154                }
155            } else {
156                dirs = new File[0];
157            }
158            return dirs;
159        }
160
161        private static URL[] getExtURLs(File[] dirs) throws IOException {
162            Vector<URL> urls = new Vector<URL>();
163            for (int i = 0; i < dirs.length; i++) {
164                String[] files = dirs[i].list();
165                if (files != null) {
166                    for (int j = 0; j < files.length; j++) {
167                        if (!files[j].equals("meta-index")) {
168                            File f = new File(dirs[i], files[j]);
169                            urls.add(getFileURL(f));
170                        }
171                    }
172                }
173            }
174            URL[] ua = new URL[urls.size()];
175            urls.copyInto(ua);
176            return ua;
177        }
178
179        /*
180         * Searches the installed extension directories for the specified
181         * library name. For each extension directory, we first look for
182         * the native library in the subdirectory whose name is the value
183         * of the system property <code>os.arch</code>. Failing that, we
184         * look in the extension directory itself.
185         */
186        public String findLibrary(String name) {
187            name = System.mapLibraryName(name);
188            URL[] urls = super.getURLs();
189            File prevDir = null;
190            for (int i = 0; i < urls.length; i++) {
191                // Get the ext directory from the URL
192                File dir = new File(urls[i].getPath()).getParentFile();
193                if (dir != null && !dir.equals(prevDir)) {
194                    // Look in architecture-specific subdirectory first
195                    // Read from the saved system properties to avoid deadlock
196                    String arch = VM.getSavedProperty("os.arch");
197                    if (arch != null) {
198                        File file = new File(new File(dir, arch), name);
199                        if (file.exists()) {
200                            return file.getAbsolutePath();
201                        }
202                    }
203                    // Then check the extension directory
204                    File file = new File(dir, name);
205                    if (file.exists()) {
206                        return file.getAbsolutePath();
207                    }
208                }
209                prevDir = dir;
210            }
211            return null;
212        }
213
214        private static AccessControlContext getContext(File[] dirs)
215            throws IOException
216        {
217            PathPermissions perms =
218                new PathPermissions(dirs);
219
220            ProtectionDomain domain = new ProtectionDomain(
221                new CodeSource(perms.getCodeBase(),
222                    (java.security.cert.Certificate[]) null),
223                perms);
224
225            AccessControlContext acc =
226                new AccessControlContext(new ProtectionDomain[] { domain });
227
228            return acc;
229        }
230    }
231
232    /**
233     * The class loader used for loading from java.class.path.
234     * runs in a restricted security context.
235     */
236    static class AppClassLoader extends URLClassLoader {
237
238        static {
239            ClassLoader.registerAsParallelCapable();
240        }
241
242        public static ClassLoader getAppClassLoader(final ClassLoader extcl)
243            throws IOException
244        {
245            final String s = System.getProperty("java.class.path");
246            final File[] path = (s == null) ? new File[0] : getClassPath(s);
247
248            // Note: on bugid 4256530
249            // Prior implementations of this doPrivileged() block supplied
250            // a rather restrictive ACC via a call to the private method
251            // AppClassLoader.getContext(). This proved overly restrictive
252            // when loading  classes. Specifically it prevent
253            // accessClassInPackage.sun.* grants from being honored.
254            //
255            return AccessController.doPrivileged(
256                new PrivilegedAction<AppClassLoader>() {
257                    public AppClassLoader run() {
258                    URL[] urls =
259                        (s == null) ? new URL[0] : pathToURLs(path);
260                    return new AppClassLoader(urls, extcl);
261                }
262            });
263        }
264
265        final URLClassPath ucp;
266
267        /*
268         * Creates a new AppClassLoader
269         */
270        AppClassLoader(URL[] urls, ClassLoader parent) {
271            super(urls, parent, factory);
272            ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
273            ucp.initLookupCache(this);
274        }
275
276        /**
277         * Override loadClass so we can checkPackageAccess.
278         */
279        public Class<?> loadClass(String name, boolean resolve)
280            throws ClassNotFoundException
281        {
282            int i = name.lastIndexOf('.');
283            if (i != -1) {
284                SecurityManager sm = System.getSecurityManager();
285                if (sm != null) {
286                    sm.checkPackageAccess(name.substring(0, i));
287                }
288            }
289
290            if (ucp.knownToNotExist(name)) {
291                // The class of the given name is not found in the parent
292                // class loader as well as its local URLClassPath.
293                // Check if this class has already been defined dynamically;
294                // if so, return the loaded class; otherwise, skip the parent
295                // delegation and findClass.
296                Class<?> c = findLoadedClass(name);
297                if (c != null) {
298                    if (resolve) {
299                        resolveClass(c);
300                    }
301                    return c;
302                }
303                throw new ClassNotFoundException(name);
304            }
305
306            return (super.loadClass(name, resolve));
307        }
308
309        /**
310         * allow any classes loaded from classpath to exit the VM.
311         */
312        protected PermissionCollection getPermissions(CodeSource codesource)
313        {
314            PermissionCollection perms = super.getPermissions(codesource);
315            perms.add(new RuntimePermission("exitVM"));
316            return perms;
317        }
318
319        /**
320         * This class loader supports dynamic additions to the class path
321         * at runtime.
322         *
323         * @see java.lang.instrument.Instrumentation#appendToSystemClassPathSearch
324         */
325        private void appendToClassPathForInstrumentation(String path) {
326            assert(Thread.holdsLock(this));
327
328            // addURL is a no-op if path already contains the URL
329            super.addURL( getFileURL(new File(path)) );
330        }
331
332        /**
333         * create a context that can read any directories (recursively)
334         * mentioned in the class path. In the case of a jar, it has to
335         * be the directory containing the jar, not just the jar, as jar
336         * files might refer to other jar files.
337         */
338
339        private static AccessControlContext getContext(File[] cp)
340            throws java.net.MalformedURLException
341        {
342            PathPermissions perms =
343                new PathPermissions(cp);
344
345            ProtectionDomain domain =
346                new ProtectionDomain(new CodeSource(perms.getCodeBase(),
347                    (java.security.cert.Certificate[]) null),
348                perms);
349
350            AccessControlContext acc =
351                new AccessControlContext(new ProtectionDomain[] { domain });
352
353            return acc;
354        }
355    }
356
357    private static class BootClassPathHolder {
358        static final URLClassPath bcp;
359        static {
360            URL[] urls;
361            if (bootClassPath != null) {
362                urls = AccessController.doPrivileged(
363                    new PrivilegedAction<URL[]>() {
364                        public URL[] run() {
365                            File[] classPath = getClassPath(bootClassPath);
366                            int len = classPath.length;
367                            Set<File> seenDirs = new HashSet<File>();
368                            for (int i = 0; i < len; i++) {
369                                File curEntry = classPath[i];
370                                // Negative test used to properly handle
371                                // nonexistent jars on boot class path
372                                if (!curEntry.isDirectory()) {
373                                    curEntry = curEntry.getParentFile();
374                                }
375                                if (curEntry != null && seenDirs.add(curEntry)) {
376                                    MetaIndex.registerDirectory(curEntry);
377                                }
378                            }
379                            return pathToURLs(classPath);
380                        }
381                    }
382                );
383            } else {
384                urls = new URL[0];
385            }
386            bcp = new URLClassPath(urls, factory);
387            bcp.initLookupCache(null);
388        }
389    }
390
391    public static URLClassPath getBootstrapClassPath() {
392        return BootClassPathHolder.bcp;
393    }
394
395    private static URL[] pathToURLs(File[] path) {
396        URL[] urls = new URL[path.length];
397        for (int i = 0; i < path.length; i++) {
398            urls[i] = getFileURL(path[i]);
399        }
400        // DEBUG
401        //for (int i = 0; i < urls.length; i++) {
402        //  System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"');
403        //}
404        return urls;
405    }
406
407    private static File[] getClassPath(String cp) {
408        File[] path;
409        if (cp != null) {
410            int count = 0, maxCount = 1;
411            int pos = 0, lastPos = 0;
412            // Count the number of separators first
413            while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
414                maxCount++;
415                lastPos = pos + 1;
416            }
417            path = new File[maxCount];
418            lastPos = pos = 0;
419            // Now scan for each path component
420            while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
421                if (pos - lastPos > 0) {
422                    path[count++] = new File(cp.substring(lastPos, pos));
423                } else {
424                    // empty path component translates to "."
425                    path[count++] = new File(".");
426                }
427                lastPos = pos + 1;
428            }
429            // Make sure we include the last path component
430            if (lastPos < cp.length()) {
431                path[count++] = new File(cp.substring(lastPos));
432            } else {
433                path[count++] = new File(".");
434            }
435            // Trim array to correct size
436            if (count != maxCount) {
437                File[] tmp = new File[count];
438                System.arraycopy(path, 0, tmp, 0, count);
439                path = tmp;
440            }
441        } else {
442            path = new File[0];
443        }
444        // DEBUG
445        //for (int i = 0; i < path.length; i++) {
446        //  System.out.println("path[" + i + "] = " + '"' + path[i] + '"');
447        //}
448        return path;
449    }
450
451    private static URLStreamHandler fileHandler;
452
453    static URL getFileURL(File file) {
454        try {
455            file = file.getCanonicalFile();
456        } catch (IOException e) {}
457
458        try {
459            return ParseUtil.fileToEncodedURL(file);
460        } catch (MalformedURLException e) {
461            // Should never happen since we specify the protocol...
462            throw new InternalError(e);
463        }
464    }
465
466    /*
467     * The stream handler factory for loading system protocol handlers.
468     */
469    private static class Factory implements URLStreamHandlerFactory {
470        private static String PREFIX = "sun.net.www.protocol";
471
472        public URLStreamHandler createURLStreamHandler(String protocol) {
473            String name = PREFIX + "." + protocol + ".Handler";
474            try {
475                Class<?> c = Class.forName(name);
476                return (URLStreamHandler)c.newInstance();
477            } catch (ReflectiveOperationException e) {
478                throw new InternalError("could not load " + protocol +
479                                        "system protocol handler", e);
480            }
481        }
482    }
483}
484
485class PathPermissions extends PermissionCollection {
486    // use serialVersionUID from JDK 1.2.2 for interoperability
487    private static final long serialVersionUID = 8133287259134945693L;
488
489    private File path[];
490    private Permissions perms;
491
492    URL codeBase;
493
494    PathPermissions(File path[])
495    {
496        this.path = path;
497        this.perms = null;
498        this.codeBase = null;
499    }
500
501    URL getCodeBase()
502    {
503        return codeBase;
504    }
505
506    public void add(java.security.Permission permission) {
507        throw new SecurityException("attempt to add a permission");
508    }
509
510    private synchronized void init()
511    {
512        if (perms != null)
513            return;
514
515        perms = new Permissions();
516
517        // this is needed to be able to create the classloader itself!
518        perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
519
520        // add permission to read any "java.*" property
521        perms.add(new java.util.PropertyPermission("java.*",
522            SecurityConstants.PROPERTY_READ_ACTION));
523
524        AccessController.doPrivileged(new PrivilegedAction<Void>() {
525            public Void run() {
526                for (int i=0; i < path.length; i++) {
527                    File f = path[i];
528                    String path;
529                    try {
530                        path = f.getCanonicalPath();
531                    } catch (IOException ioe) {
532                        path = f.getAbsolutePath();
533                    }
534                    if (i == 0) {
535                        codeBase = Launcher.getFileURL(new File(path));
536                    }
537                    if (f.isDirectory()) {
538                        if (path.endsWith(File.separator)) {
539                            perms.add(new FilePermission(path+"-",
540                                SecurityConstants.FILE_READ_ACTION));
541                        } else {
542                            perms.add(new FilePermission(
543                                path + File.separator+"-",
544                                SecurityConstants.FILE_READ_ACTION));
545                        }
546                    } else {
547                        int endIndex = path.lastIndexOf(File.separatorChar);
548                        if (endIndex != -1) {
549                            path = path.substring(0, endIndex+1) + "-";
550                            perms.add(new FilePermission(path,
551                                SecurityConstants.FILE_READ_ACTION));
552                        } else {
553                            // XXX?
554                        }
555                    }
556                }
557                return null;
558            }
559        });
560    }
561
562    public boolean implies(java.security.Permission permission) {
563        if (perms == null)
564            init();
565        return perms.implies(permission);
566    }
567
568    public java.util.Enumeration<Permission> elements() {
569        if (perms == null)
570            init();
571        synchronized (perms) {
572            return perms.elements();
573        }
574    }
575
576    public String toString() {
577        if (perms == null)
578            init();
579        return perms.toString();
580    }
581}

Launcher.java这个类私有了构造方法,在成员变量中进行了实例化。同时定义了一个ClasLoader loader的变量,用于保存系统类加载器。

1private static Launcher launcher = new Launcher();
2...
3private ClassLoader loader;

在构造方法中主要有3个动作:

  1. 实例化扩展类加载器
1 // Create the extension class loader
2        ClassLoader extcl;
3        try {
4            extcl = ExtClassLoader.getExtClassLoader();
5        } catch (IOException e) {
6            throw new InternalError(
7                "Could not create extension class loader", e);
8        }
  1. 实例化系统类加载器
1 try {
2            loader = AppClassLoader.getAppClassLoader(extcl);
3        } catch (IOException e) {
4            throw new InternalError(
5                "Could not create application class loader", e);
6        }
7        
  1. 将系统类加载器设置到线程上下文类加载器
1// Also set the context class loader for the primordial thread.
2        Thread.currentThread().setContextClassLoader(loader);

ExtClassLoader和AppClassLoader是URLClassLoader的子类,类的主要加载逻辑已由URLClassLoader和器父类加载器完成了,这2个子类主要定义了类的加载路径。

对于ExtClassLoader,处理加载路径逻辑:

 1        private static File[] getExtDirs() {
 2            String var0 = System.getProperty("java.ext.dirs");
 3            File[] var1;
 4            if (var0 != null) {
 5                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
 6                int var3 = var2.countTokens();
 7                var1 = new File[var3];
 8
 9                for(int var4 = 0; var4 < var3; ++var4) {
10                    var1[var4] = new File(var2.nextToken());
11                }
12            } else {
13                var1 = new File[0];
14            }
15
16            return var1;
17        }

从系统参数"java.ext.dirs"值获取加载的路径值,️以分隔符为标记拆分为File数组,包含了扩展类加载器加载的全部路径。

对于AppClassLoader,处理逻辑:

1  final String s = System.getProperty("java.class.path");
2            final File[] path = (s == null) ? new File[0] : getClassPath(s);

类似地,从classpath中解析出所有的加载路径。

从AppClassLoader的构造方法看出,它以扩展类加载器为父类加载器。

1 /*
2         * Creates a new AppClassLoader
3         */
4        AppClassLoader(URL[] urls, ClassLoader parent) {
5            super(urls, parent, factory);
6            ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
7            ucp.initLookupCache(this);
8        }