Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/383.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 类加载器如何在清单类路径中加载类引用?_Java_Jar_Classpath_Classloader - Fatal编程技术网

Java 类加载器如何在清单类路径中加载类引用?

Java 类加载器如何在清单类路径中加载类引用?,java,jar,classpath,classloader,Java,Jar,Classpath,Classloader,我使用maven构建了一个带有外部类路径的jar 当我使用java-jar artifact.jar运行该jar时,它能够从该主jar和libs目录中的所有jar加载类 但是,如果我询问系统属性java.class.path,它将只列出主jar。如果我向系统类加载器请求其URL(ClassLoader.getSystemClassLoader().getURLs()),它也只会返回主jar。如果我向某个库中包含的任何类请求其类加载器,它将返回系统类加载器 系统类加载器如何加载这些类 为了从这些库

我使用maven构建了一个带有外部类路径的jar

当我使用
java-jar artifact.jar
运行该jar时,它能够从该主jar和libs目录中的所有jar加载类

但是,如果我询问系统属性
java.class.path
,它将只列出主jar。如果我向系统类加载器请求其URL(
ClassLoader.getSystemClassLoader().getURLs()
),它也只会返回主jar。如果我向某个库中包含的任何类请求其类加载器,它将返回系统类加载器

系统类加载器如何加载这些类


为了从这些库中加载类,它必须对这些库有一些了解。有没有办法要求它提供这种“扩展”类路径?

简单的回答是,该实现是Sun内部工作的一部分,不能通过公共途径获得
getURL()
将只返回传入的URL。有一个更长的答案,但它只是为大胆

通过使用调试器单步执行Oracle JVM 8,我了解了与OpenJDK6几乎相同的结构,您可以看到它在哪里加载类路径

基本上,类加载器保留一堆尚未解析到内存中的URL。当要求加载一个类时,它将弹出堆栈中的URL,将它们作为类文件或jar文件加载,如果它们是jar文件,它将读取清单并将类路径条目推送到堆栈上。每次处理文件时,它都会将加载该文件的“加载器”添加到加载器映射(如果没有其他内容,则确保不会多次处理同一文件)

如果您确实有动机(不推荐)使用以下工具,您可以访问此地图:

在一个虚拟设置上运行它,其中dummy-plugin.jar引用external.jar(在dummy-plugin.jar的清单中),我得到以下结果:

1) 创建类加载器后(加载任何类之前):

2) 从dummy-plugin.jar加载类后:

urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
getSecretUrlsStack=[file:.../external.jar]
getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb}
3) 从external.jar加载类后:

urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
getSecretUrlsStack=[]
getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb, file:.../external.jar=sun.misc.URLClassPath$JarLoader@2d8e6db6}
奇怪的是,这似乎与以下事实背道而驰:

默认情况下,加载的类仅授予 访问创建URLClassLoader时指定的URL


使用反射访问system class loader实例中的私有字段会出现以下几个问题:

  • 安全管理员可以禁止登录
  • 解决方案取决于实现
另一个“侵入性”较小的解决方案是:

  • 对于给定的类加载器,枚举所有可用的清单
    cl.getResources(“META-INF/manifest.MF”)
    。这些清单可以是由当前类加载器或其提升类加载器管理的JAR清单
  • 对其父类加载器执行相同的操作
  • 返回(1)中但不在(2)中的那些清单的JAR集
  • 该方法工作的唯一要求是类路径中的JAR必须有一个清单才能返回(要求不多)

    /**
    *返回URL的搜索路径,以加载
    *指定的类装入器,包括在
    *{@code Class path}可执行jar清单的头,在
    *类加载器是系统类加载器的情况。
    *
    *注意:这些最后的罐子不是由供应商退回的
    *{@link java.net.URLClassLoader#getURLs()}。
    *

    *@param cl *@返回 */ 公共静态URL[]获取URL(URLClassLoader cl){ 如果(cl.getParent()==null | |!(cl.getParent()) URLClassLoader的实例){ 返回cl.getURL(); } Set urlSet=new LinkedHashSet(); URL[]URL=cl.getURL(); URL[]urlsFromManifest=GetJarURLSFROMMANIFEST(cl); URLClassLoader parentCl=(URLClassLoader)cl.getParent(); URL[]ancestorUrls=getJarUrlsFromManifests(parentCl); for(int i=0;i
    您能打开jar并查看生成的清单以了解发生了什么吗?清单中有一个类路径条目,其中列出了libs目录中的所有jar。-一模一样。干得好!但在安全经理在场时可能会出现问题
    urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
    getSecretUrlsStack=[file:.../external.jar]
    getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb}
    
    urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
    getSecretUrlsStack=[]
    getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb, file:.../external.jar=sun.misc.URLClassPath$JarLoader@2d8e6db6}
    
    /**
     * Returns the search path of URLs for loading classes and resources for the 
     * specified class loader, including those referenced in the 
     * {@code Class-path} header of the manifest of a executable jar, in the 
     * case of class loader being the system class loader. 
     * <p>
     * Note: These last jars are not returned by 
     * {@link java.net.URLClassLoader#getURLs()}.
     * </p>
     * @param cl
     * @return 
     */
    public static URL[] getURLs(URLClassLoader cl) {
        if (cl.getParent() == null || !(cl.getParent() 
                instanceof URLClassLoader)) {
            return cl.getURLs();
        }
        Set<URL> urlSet = new LinkedHashSet();
        URL[] urLs = cl.getURLs();
        URL[] urlsFromManifest = getJarUrlsFromManifests(cl);
        URLClassLoader parentCl = (URLClassLoader) cl.getParent();
        URL[] ancestorUrls = getJarUrlsFromManifests(parentCl);
    
        for (int i = 0; i < urlsFromManifest.length; i++) {
            urlSet.add(urlsFromManifest[i]);
        }
        for (int i = 0; i < ancestorUrls.length; i++) {
            urlSet.remove(ancestorUrls[i]);
        }
        for (int i = 0; i < urLs.length; i++) {
            urlSet.add(urLs[i]);
        }
        return urlSet.toArray(new URL[urlSet.size()]);
    }
    
    /**
     * Returns the URLs of those jar managed by this classloader (or its 
     * ascendant classloaders) that have a manifest
     * @param cl
     * @return 
     */
    private static URL[] getJarUrlsFromManifests(ClassLoader cl) {
        try {
            Set<URL> urlSet = new LinkedHashSet();
            Enumeration<URL> manifestUrls = 
                    cl.getResources("META-INF/MANIFEST.MF");
            while (manifestUrls.hasMoreElements()) {
                try {
                    URL manifestUrl = manifestUrls.nextElement();
                    if(manifestUrl.getProtocol().equals("jar")) {
                        urlSet.add(new URL(manifestUrl.getFile().substring(0, 
                                manifestUrl.getFile().lastIndexOf("!"))));
                    }
                } catch (MalformedURLException ex) {
                    throw new AssertionError();
                }
            }
            return urlSet.toArray(new URL[urlSet.size()]);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }