Java 类加载器如何在清单类路径中加载类引用?
我使用maven构建了一个带有外部类路径的jar 当我使用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。如果我向某个库中包含的任何类请求其类加载器,它将返回系统类加载器 系统类加载器如何加载这些类 为了从这些库
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清单/**
*返回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);
}
}