URLClassLoader源码分析。

/**
 * 该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。
 * 这里假定任何以 '/' 结束的 URL 都是指向目录的。
 * 如果不是以该字符结束,则认为该 URL 指向一个将根据需要打开的 JAR 文件。 
 * 创建 URLClassLoader 实例的 AccessControlContext 线程将在后续加载类和资源时使用。 
 * 为加载的类默认授予只能访问 URLClassLoader 创建时指定的 URL 的权限。
 */
public class URLClassLoader extends SecureClassLoader implements Closeable {
	/*
	 * 继承自SecureClassLoader,支持从jar文件和文件夹中获取class
	 * 实现了Closeable接口,实现在try 中自动释放资源,但扑捉不了.close()异常
	 */
	// 类和资源的搜索路径
    private final URLClassPath ucp;

	// 加载类和资源时使用的上下文
    private final AccessControlContext acc;

	/**
	 * 为给定的 URL 构造新 URLClassLoader。
	 * 首先在指定的父类加载器中搜索 URL,然后按照为类和资源指定的顺序搜索 URL。
	 * 这里假定任何以 '/' 结束的 URL 都是指向目录的。
	 * 如果不是以该字符结束,则认为该 URL 指向一个将根据需要下载和打开的 JAR 文件。 
	 * 如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保允许创建类加载器。 
	 * @param urls	从其位置加载类和资源的 UR
	 * @param parent	用于委托的父类加载器
	 */
    public URLClassLoader(URL[] urls, ClassLoader parent) {
        super(parent);
        // 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            // 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
            security.checkCreateClassLoader();
        }
        // 获取当前调用上下文(包括当前 Thread 的继承 AccessControlContext)的“快照”,并将其置于 AccessControlContext 对象中。
        this.acc = AccessController.getContext();
        // 构造类和资源的搜索路径的对象
        ucp = new URLClassPath(urls, acc);
    }
    /**
     * 供FactoryURLClassLoader调用
     * @param urls
     * @param parent
     * @param acc
     */
    URLClassLoader(URL[] urls, ClassLoader parent,
                   AccessControlContext acc) {
        super(parent);
	// 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
		// 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
            security.checkCreateClassLoader();
        }
        this.acc = acc;
	// 构造类和资源的搜索路径的对象
        ucp = new URLClassPath(urls, acc);
    }

    /**
     * 使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。
     * 首先在父类加载器中搜索 URL,然后按照为类和资源指定的顺序搜索 URL。
     * 这里假定任何以 '/' 结束的 URL 都是指向目录的。
     * 如果不是以该字符结束,则认为该 URL 指向一个将根据需要下载和打开的 JAR 文件。 
     * 如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保允许创建类加载器。
     * @param urls	从其位置加载类和资源的 URL 
     */
    public URLClassLoader(URL[] urls) {
        super();
     // 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
        	// 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
            security.checkCreateClassLoader();
        }
        // 获取当前调用上下文(包括当前 Thread 的继承 AccessControlContext)的“快照”,并将其置于 AccessControlContext 对象中。
        this.acc = AccessController.getContext();
     // 构造类和资源的搜索路径的对象
        ucp = new URLClassPath(urls, acc);
    }
    /**
     * 供FactoryURLClassLoader调用
     * @param urls
     * @param acc
     */
    URLClassLoader(URL[] urls, AccessControlContext acc) {
        super();
     // 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
        	// 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
            security.checkCreateClassLoader();
        }

        this.acc = acc;
        // 构造类和资源的搜索路径的对象
        ucp = new URLClassPath(urls, acc);
    }

    /**
     * 为指定的 URL、父类加载器和 URLStreamHandlerFactory 创建新 URLClassLoader。
     * 该父参数将充当委托的父类加载器。该工厂参数将充当创建新 jar URL 时获取协议处理程序的流处理程序工厂。 
     * 如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保允许创建类加载器。

     * @param urls	从其位置加载类和资源的 URL
     * @param parent	用于委托的父类加载器
     * @param factory	创建 URL 时使用的 URLStreamHandlerFactory
     */
    public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
        super(parent);
	// 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
        	// 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
            security.checkCreateClassLoader();
        }
	// 获取当前调用上下文(包括当前 Thread 的继承 AccessControlContext)的“快照”,并将其置于 AccessControlContext 对象中。
        acc = AccessController.getContext();
	// 构造类和资源的搜索路径的对象
        ucp = new URLClassPath(urls, factory, acc);
    }

    /*
     * 用于跟踪可关闭本地资源(JarFiles或FileInputStreams)的映射(用作set)。
     * 我们不关心Http资源,因为它们不需要关闭。
     * 
     * 如果资源来自jar文件,我们将保留对JarFile对象的(弱)引用
     * 如果调用URLClassLoader.close(),则关闭。
     * 由于jar文件缓存,每个底层jar文件通常只有一个JarFile对象。
     * 
     * 对于文件资源,这可能是一种不太常见的情况,我们必须保持对每个流的弱引用。
     */
    private WeakHashMap<Closeable,Void>
        closeables = new WeakHashMap<>();

    /**
     * 返回用于读取指定资源的输入流。
     * 如果关闭此加载程序,则此方法打开的任何资源都将关闭。
     * 搜索顺序在 ClassLoader.getResource(String)的文档中描述。
     * @param name 资源名称
     */
    public InputStream getResourceAsStream(String name) {
    	// 查找具有给定名称的资源
        URL url = getResource(name);
        try {
            if (url == null) {
                return null;
            }
            // 获取一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 
            URLConnection urlc = url.openConnection();
            // 获取从此打开的连接读取的输入流。
            InputStream is = urlc.getInputStream();
            if (urlc instanceof JarURLConnection) {// 如果是 jarURL
                JarURLConnection juc = (JarURLConnection)urlc;
                // 获取此连接的 JAR 文件
                JarFile jar = juc.getJarFile();
                synchronized (closeables) {
                    if (!closeables.containsKey(jar)) { //保存JarFiles
                        closeables.put(jar, null);
                    }
                }
            } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) { // 如果是fileURL
                synchronized (closeables) {
                    closeables.put(is, null); // 保存FileInputStreams
                }
            }
            return is;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 关闭这个URLClassLoader,这样它就不能再用来加载由这个加载器定义的新类或资源。
     * 委托层次结构中由该加载器的任何父类定义的类和资源仍然可以访问。
     * 而且,已经加载的任何类或资源仍然可以访问。
     * 
     * 对于jar:和file: URLs,它还关闭它打开的所有文件。
     * 如果在调用close()方法时,另一个线程正在加载类,则该加载的结果是未定义的。
     * 
     * 该方法通过在内部捕获IOException,尽力关闭所有打开的文件。
     * 未检查的异常和错误不会被捕获。在已经关闭的加载程序上调用close没有效果。
     */
    public void close() throws IOException {
		// 获取系统安全接口
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
        	// 检验是否允许访问该类的父类加载器
            security.checkPermission(new RuntimePermission("closeClassLoader"));
        }
        List<IOException> errors = ucp.closeLoaders();

        // 现在关闭任何剩余的流。

        synchronized (closeables) {
            Set<Closeable> keys = closeables.keySet();
            for (Closeable c : keys) {
                try {
                	// 关闭此流并释放与此流关联的所有系统资源
                    c.close();
                } catch (IOException ioex) {
                    errors.add(ioex);
                }
            }
            closeables.clear();
        }

        if (errors.isEmpty()) {
            return;
        }

        IOException firstex = errors.remove(0);

        // 禁止任何剩余异常

        for (IOException error: errors) {
            firstex.addSuppressed(error);
        }
        throw firstex;
    }

	/**
	 * 将指定的 URL 添加到 URL 列表中,以便搜索类和资源。
	 * @param url	将添加到 URL 搜索路径中的 URL
	 */
    protected void addURL(URL url) {
        ucp.addURL(url);
    }

    /**
     * 返回用于加载类和资源的 URL 搜索路径。
     * 这包括为构造方法指定的原始 URL 列表,以及由 addURL() 方法后续添加的 URL。 
     * @return	用于加载类和资源的 URL 搜索路径。
     */
    public URL[] getURLs() {
        return ucp.getURLs();
    }

    /**
     * 通过 URL 搜索路径查找并加载具有指定名称的类。
     * 只有在找到该类后,才能根据需要加载和打开任何指向 JAR 文件的 URL。
     * 覆盖类 ClassLoader 中的 findClass()
     * @param name 类的名称 
     */
    protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            	/*
            	 * 通过指定的 AccessControlContext 启用和限制特权,执行指定的 PrivilegedExceptionAction。
            	 */
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        	// 获取路径
                        String path = name.replace('.', '/').concat(".class");
                             // 获取资源
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                	// 使用从指定资源获得的类字节定义类。
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

    /**
     * 使用指定的包名检索包。如果非空,请使用指定的源代码和Manifest。
     * @param pkgname	包的名称
     * @param man	包含包的版本和密封信息的 Manifest
     * @param url	包的代码源 url,或者如果没有,则为 null 
     * @return
     */
    private Package getAndVerifyPackage(String pkgname,
                                        Manifest man, URL url) {
		// 获取由此类加载器或其任何祖先所定义的 Package
        Package pkg = getPackage(pkgname);
        if (pkg != null) {
			// 如果此包是密封的,则返回 ture
            if (pkg.isSealed()) {
				// 验证code source URL是否相同。
                if (!pkg.isSealed(url)) {
                    throw new SecurityException(
                        "sealing violation: package " + pkgname + " is sealed");
                }
            } else {
				// 确保我们没有试图在此源代码URL处密封该包。
                // at this code source URL.
                if ((man != null) && isSealed(pkgname, man)) {
                    throw new SecurityException(
                        "sealing violation: can't seal package " + pkgname +
                        ": already loaded");
                }
            }
        }
        return pkg;
    }
    /**
     * 被虚拟机调用来定义从CDS archive文件中加载的类
     * @param pkgname	包的名称
     * @param man	包含包的版本和密封信息的 Manifest
     * @param url	包的代码源 url,或者如果没有,则为 null 
     */
    private void definePackageInternal(String pkgname, Manifest man, URL url)
    {
    	// 当指定的包名检索包为null时
        if (getAndVerifyPackage(pkgname, man, url) == null) {
            try {
                if (man != null) {
                	// 用该 ClassLoader 中的名称定义一个新包。urlClassLoader类的
                    definePackage(pkgname, man, url);
                } else {
                	// 根据 name 在此 ClassLoader 中定义包。classLoader类的
                    definePackage(pkgname, null, null, null, null, null, null, null);
                }
            } catch (IllegalArgumentException iae) {
                // 具有并行能力的类装入器:在条件下重新验证
            	// 当指定的包名检索包为null时抛异常,不应该发生
                if (getAndVerifyPackage(pkgname, man, url) == null) {
                    // Should never happen
                    throw new AssertionError("Cannot find package " +
                                             pkgname);
                }
            }
        }
    }

    /**
     * 使用从指定资源获得的类字节定义类。
     * 在使用结果类之前,必须先解析它。
     * @param name	类的名称
     * @param res	根据url获取的资源
     * @return
     * @throws IOException
     */
    private Class<?> defineClass(String name, Resource res) throws IOException {
    	// 获取最准确的可用系统计时器的当前值,以毫微秒为单位。
        long t0 = System.nanoTime();
        int i = name.lastIndexOf('.');
        URL url = res.getCodeSourceURL();
        if (i != -1) {
            String pkgname = name.substring(0, i);
            // 检查包是否已加载。
            // 获取包含包的版本和密封信息的 Manifest
            Manifest man = res.getManifest();
            // 加载类
            definePackageInternal(pkgname, man, url);
        }
        // 现在读取类字节并定义类
        java.nio.ByteBuffer bb = res.getByteBuffer();
        if (bb != null) {
            // 使用 (direct) ByteBuffer:
        	// 获取代码签名者集合。
            CodeSigner[] signers = res.getCodeSigners();
            // 构造一个 CodeSource 并将其与指定位置和代码签名者集合相关联。
            CodeSource cs = new CodeSource(url, signers);
            sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
            // 使用可选的 CodeSource 将 ByteBuffer 转换为 Class 类的实例。
            return defineClass(name, bb, cs);
        } else {
        	// 获取字节
            byte[] b = res.getBytes();
            // 必须在读取字节后读取证书。
            CodeSigner[] signers = res.getCodeSigners();
         // 构造一个 CodeSource 并将其与指定位置和代码签名者集合相关联。
            CodeSource cs = new CodeSource(url, signers);
            sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
            // 使用可选的 CodeSource 将 byte 数组转换为 Class 类的实例。
            return defineClass(name, b, 0, b.length, cs);
        }
    }

    /**
     * 用该 ClassLoader 中的名称定义一个新包。该指定的 Manifest 中包含的属性将用于包含包版本和密封信息。
     * 对于密封的包,此附加 URL 从加载该包的 URL 指定代码源 URL。
     * @param name	包的名称
     * @param man	包含包的版本和密封信息的 Manifest
     * @param url	包的代码源 url,或者如果没有,则为 null 
     * @return	新定义的 Package 对象 
     * @throws IllegalArgumentException
     */
    protected Package definePackage(String name, Manifest man, URL url)
        throws IllegalArgumentException
    {
    		// 获取路径
        String path = name.replace('.', '/').concat("/");
        String specTitle = null, specVersion = null, specVendor = null;
        String implTitle = null, implVersion = null, implVendor = null;
        String sealed = null;
        URL sealBase = null;
            // 根据路径获取 Attributes(Attributes 类将 Manifest 属性名称映射到关联的字符串值)
        Attributes attr = man.getAttributes(path);
        if (attr != null) {
            	// Specification-Title 的 Name 对象给出了用于包的版本控制的属性
            specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
                // Specification-Version 的 Name 对象给出了用于包的版本控制的属性。 
            specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
                // Specification-Vendor 的 Name 对象给出了用于包的版本控制的属性。
            specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
                // Implementation-Title 的 Name 对象给出了用于包的版本控制的属性。
            implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
                // Implementation-Version 的 Name 对象给出了用于包的版本控制的属性。
            implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
                // Implementation-Vendor 的 Name 对象给出了用于包的版本控制的属性。 
            implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
                // Sealed 的 Name 对象给出了用于密封的属性。
            sealed      = attr.getValue(Name.SEALED);
        }
            // 返回 Manifest 的主 Attributes
        attr = man.getMainAttributes();
        if (attr != null) {
            if (specTitle == null) {
                	// Specification-Title 的 Name 对象给出了用于包的版本控制的属性
                specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
            }
            if (specVersion == null) {
                	 // Specification-Version 的 Name 对象给出了用于包的版本控制的属性。 
                specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
            }
            if (specVendor == null) {
                	// Specification-Vendor 的 Name 对象给出了用于包的版本控制的属性。
                specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
            }
            if (implTitle == null) {
                	// Implementation-Title 的 Name 对象给出了用于包的版本控制的属性。
                implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
            }
            if (implVersion == null) {
                	 // Implementation-Version 的 Name 对象给出了用于包的版本控制的属性。
                implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
            }
            if (implVendor == null) {
                	// Implementation-Vendor 的 Name 对象给出了用于包的版本控制的属性。 
                implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
            }
            if (sealed == null) {
                	// Sealed 的 Name 对象给出了用于密封的属性。
                sealed = attr.getValue(Name.SEALED);
            }
        }
        if ("true".equalsIgnoreCase(sealed)) {
            sealBase = url;
        }
            // 根据 name 在此 ClassLoader 中定义包。
        return definePackage(name, specTitle, specVersion, specVendor,
                             implTitle, implVersion, implVendor, sealBase);
    }

    /**
     * 如果指定的包名根据给定的Manifest密封,则返回true。
     * @param name	包的名称
     * @param man	包含包的版本和密封信息的 Manifest
     * @return
     */
    private boolean isSealed(String name, Manifest man) {
    	// 获取路径
        String path = name.replace('.', '/').concat("/");
        // 根据路径获取 Attributes(Attributes 类将 Manifest 属性名称映射到关联的字符串值)
        Attributes attr = man.getAttributes(path);
        String sealed = null;
        if (attr != null) {
        	// Sealed 的 Name 对象给出了用于密封的属性。
            sealed = attr.getValue(Name.SEALED);
        }
        if (sealed == null) {
       	// 获取 Manifest 的主 Attributes不为空
            if ((attr = man.getMainAttributes()) != null) {
            	// Sealed 的 Name 对象给出了用于密封的属性
                sealed = attr.getValue(Name.SEALED);
            }
        }
        return "true".equalsIgnoreCase(sealed);
    }

    /**
     * 在 URL 搜索路径中查找具有指定名称的资源。
     * 覆盖:类 ClassLoader 中的 findResource()方法
     * @param name 资源的名称
     */
    public URL findResource(final String name) {
        /*
         * 查找类的限制同样适用于资源
         */
        URL url = AccessController.doPrivileged(
            new PrivilegedAction<URL>() {
                public URL run() {
                    return ucp.findResource(name, true);
                }
            }, acc);

        return url != null ? ucp.checkURL(url) : null;
    }

    /**
     * 返回表示 URL 搜索路径中具有指定名称的所有资源的 URL 枚举。 
     * @param name 资源的名称
     */
    public Enumeration<URL> findResources(final String name)
        throws IOException
    {
        // 获取URL枚举
        final Enumeration<URL> e = ucp.findResources(name, true);

        return new Enumeration<URL>() {
            private URL url = null;

            private boolean next() {
                if (url != null) {
                    return true;
                }
                do {
                    	// 通过指定的 AccessControlContext 启用和限制特权,执行指定的 PrivilegedAction。
                    URL u = AccessController.doPrivileged(
                        new PrivilegedAction<URL>() {
                            public URL run() {
                                	// 测试此枚举是否包含更多的元素
                                if (!e.hasMoreElements())
                                    return null;
                                    // 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
                                return e.nextElement();
                            }
                        }, acc);
                    if (u == null)
                        break;
                    url = ucp.checkURL(u);
                } while (url == null);
                return url != null;
            }
                /**
                 * 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
                 */
            public URL nextElement() {
                if (!next()) {
                    throw new NoSuchElementException();
                }
                URL u = url;
                url = null;
                return u;
            }
                /**
                 * 测试此枚举是否包含更多的元素
                 */
            public boolean hasMoreElements() {
                return next();
            }
        };
    }

    /**
     * 返回给定 codesource 对象的权限。
     * 该方法的实现首先调用 super.getPermissions,然后基于 codesource 的 URL 添加权限。 
     * 如果此 URL 的协议为 "jar",那么授予的权限将基于 Jar 文件 URL 所请求的权限。 
     * 如果协议为 "file",并且路径指定了某个文件,则要授予对该文件的读权限。
     * 如果协议为 "file",并且路径是一个目录,则要授予该目录中的所有文件及其(递归)子目录中包含的所有文件读权限。 
     * 如果协议不是 "file",则允许连接到和接收来自 URL 主机的连接。 
     * 覆盖类 :SecureClassLoader 中的 getPermissions
     * @param codesource the codesource
     */
    protected PermissionCollection getPermissions(CodeSource codesource)
    {
        // 获取给定的 CodeSource 对象的权限。
        PermissionCollection perms = super.getPermissions(codesource);
        // 返回与此 CodeSource 关联的位置
        URL url = codesource.getLocation();

        Permission p;
        URLConnection urlConnection;

        try {
        	// 获取一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
            urlConnection = url.openConnection();
            // 获取一个权限对象,其代表建立此对象表示的连接所需的权限。
            p = urlConnection.getPermission();
        } catch (java.io.IOException ioe) {
            p = null;
            urlConnection = null;
        }
        // FilePermission:此类表示对文件和目录的访问
        if (p instanceof FilePermission) {
            // 如果权限末尾有一个分隔符char,这意味着codebase是一个目录,我们需要添加一个额外的权限来递归读取
        	// 返回此 Permission 的名称
            String path = p.getName();
            // File.separator:与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
            // 检查路径是否以(File.separator)后缀结尾。
            if (path.endsWith(File.separator)) {
                path += "-";
                // 创建具有指定操作的新 FilePermission 对象,path 是文件或目录的路径名,
                // actions 包含对文件或目录授予的所需操作的列表,该列表由逗号分隔。
                // 读的权限
                p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
            }
            // 获取获取此 URL 的协议名称是否等于file
        } else if ((p == null) && (url.getProtocol().equals("file"))) {
        	// 获取路径
            String path = url.getFile().replace('/', File.separatorChar);
            path = ParseUtil.decode(path);
            // File.separator:与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
            // 检查路径是否以(File.separator)后缀结尾。
            if (path.endsWith(File.separator))
                path += "-";
            // 创建具有指定操作的新 FilePermission 对象,path 是文件或目录的路径名,
            // actions 包含对文件或目录授予的所需操作的列表,该列表由逗号分隔。
            // 读的权限
            p =  new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
        } else {
            /**
             * 没有从'file:' URL加载,因此我们希望在确保主机是正确的且有效后,
             * 授予类从远程主机连接和接受的权限。
             */
            URL locUrl = url;
            if (urlConnection instanceof JarURLConnection) { // 如果是JARURL
            	// 获取 此连接的 Jar 文件的 URL
                locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
            }
            // 获取此 URL 的主机名(如果适用)。
            // 主机的格式遵守 RFC 2732,即对于一个字面值 IPv6 地址,该方法将返回括在方括号 ('[' 和 ']') 中的 IPv6 地址。
            String host = locUrl.getHost();
            if (host != null && (host.length() > 0))
            	// 创建带指定动作的新 SocketPermission 对象。
            	// 主机被表达为 DNS 名称或数字 IP 地址。
            	// 可以选择支持端口还是端口范围(用冒号分隔 DNS 名称或 IP 地址)。
                p = new SocketPermission(host,
                                         SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
        }

        // 确保创建这个类加载器的人有这个权限

        if (p != null) {
        	// 获取系统安全接口
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                final Permission fp = p;
                /*
            	 * 通过指定的 AccessControlContext 启用和限制特权,执行指定的 PrivilegedAction。
            	 */
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() throws SecurityException {
                    	// 检验是否允许访问该类的父类加载器
                        sm.checkPermission(fp);
                        return null;
                    }
                }, acc);
            }
            // 授予 codesource 的权限
            perms.add(p);
        }
        return perms;
    }

    /**
     * 为指定的 URL 和父类加载器创建新 URLClassLoader 实例。
     * 如果安装了安全管理器,该方法返回的 URLClassLoader 的 loadClass 方法将在加载该类之前
     * 调用 SecurityManager.checkPackageAccess 方法。 
     * @param urls	用于搜索类和资源的 URL
     * @param parent	用于委托的父类加载器
     * @return	结果类加载器
     */
    public static URLClassLoader newInstance(final URL[] urls,
                                             final ClassLoader parent) {
		// 保存调用者的上下文
        final AccessControlContext acc = AccessController.getContext();
		// 需要一个特权块来创建类装入器
        URLClassLoader ucl = AccessController.doPrivileged(
            new PrivilegedAction<URLClassLoader>() {
                public URLClassLoader run() {
                    // 为指定的 URL 和父类加载器
                    return new FactoryURLClassLoader(urls, parent, acc);
                }
            });
        return ucl;
    }

    /**
     * 为指定的 URL 和默认的父类加载器创建新 URLClassLoader 实例。
     * 如果安装了安全管理器,该方法返回的 URLClassLoader 的 loadClass 方法将在加载该类之前
     * 调用 SecurityManager.checkPackageAccess。
     * @param urls	用于搜索类和资源的 URL 
     * @return	结果类加载器
     */
    public static URLClassLoader newInstance(final URL[] urls) {
        // 保存调用者的上下文
        final AccessControlContext acc = AccessController.getContext();
        // 需要一个特权块来创建类装入器
        URLClassLoader ucl = AccessController.doPrivileged(
            new PrivilegedAction<URLClassLoader>() {
                public URLClassLoader run() {
                    // 为指定的 URL 和父类加载器
                    return new FactoryURLClassLoader(urls, acc);
                }
            });
        return ucl;
    }

    static {
        sun.misc.SharedSecrets.setJavaNetAccess (
            new sun.misc.JavaNetAccess() {
                public URLClassPath getURLClassPath (URLClassLoader u) {
                    return u.ucp;
                }

                public String getOriginalHostName(InetAddress ia) {
                    return ia.holder.getOriginalHostName();
                }
            }
        );
     // 将调用者注册为并行的。
        ClassLoader.registerAsParallelCapable();
    }
}

final class FactoryURLClassLoader extends URLClassLoader {

    static {
    	// 将调用者注册为并行的。
        ClassLoader.registerAsParallelCapable();
    }
    /**
     * 构建URLClassLoaderTest对象
     * @param urls
     * @param parent
     * @param acc
     */
    FactoryURLClassLoader(URL[] urls, ClassLoader parent,
                          AccessControlContext acc) {
        super(urls, parent, acc);
    }
    /**
     * 构建URLClassLoaderTest对象
     * @param urls
     * @param acc
     */
    FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
        super(urls, acc);
    }

    public final Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // 首先检查我们是否有访问包的权限。一旦我们添加了对导出包的支持,这种情况就会消失。
    	 // 获取系统安全接口 
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
            	// 检查是否有访问包的权限
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
        // 使用指定的二进制名称来加载类。classLoader类的
        return super.loadClass(name, resolve);
    }
}

参考:JDK 1.6 API

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件求生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值