繁体   English   中英

如何从类加载器获取类路径?

[英]How to get classpath from classloader?

我正在使用一些第三方代码,当给定“-classpath”命令行参数时,它不会设置 java.class.path,而是只创建一个类加载器,将命令行上指定的类路径上的项目的所有 url 添加到类加载器,然后将其设置为上下文类加载器。 在我编写的这段代码的插件类中,我得到了这个类加载器的一个实例,并且以某种方式需要使用它来获取底层类路径,以便我可以在调用 JavaCompiler.getTask(... ) 并即时编译一些其他代码。 然而,似乎没有办法从 ClassLoader 中获取 ClassPath,并且由于 java.class.path 未设置,我似乎无法访问应用程序最初用...调用的底层类路径......有什么想法吗?

如果类加载器使用 URL,则它必须是URLClassloader 您可以访问的是 URL,它定义了它的类路径及其父ClassLoader

要获取 URL,只需执行以下操作:

((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()

今天枚举类路径的最干净的方法是使用ClassGraph库(我是作者)。 请注意,如果您希望您的代码在今天具有可移植性,那么读取java.class.path属性或调用((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()的旧答案严重不足,因为许多运行时环境不再使用java.class.path ,和/或它们的类加载器不扩展URLClassLoader ,和/或它们使用一些模糊的机制来扩展类路径(如 jar 清单文件中的Class-Path:属性),和/或您的代码可能在 JDK 9+ 中作为模块运行(或者您的代码将在 JDK9+ 中的传统类路径上运行,但传统类路径的标准 JDK 类加载器甚至不再扩展URLClassLoader )。

ClassGraph 自动处理大量的类路径规范机制和类加载器实现 对于大多数支持的类加载器,已经为 ClassGraph 编写了自定义反射代码以从类加载器获取类路径(这是必需的,因为ClassLoader API 没有任何获取类路径的标准机制)。 您可以为此编写自己的代码,但它可能只支持URLClassLoader而不会花费大量精力——所以最好只使用 ClassGraph,因为工作已经为您完成了。

要获取类路径(以及添加到模块路径的非系统模块化 jar),只需调用:

List<URI> classpath = new ClassGraph().getClasspathURIs();

请注意,在 Java 9+ 中,模块(或 jlink 的 jar)可能会出现在带有jrt: URI 的列表中,您不能直接使用它做太多事情(除了使用 ClassGraph 从中读取资源和类,因为 ClassGraph 可以另外使用 JPMS API 来访问这些资源和类)。 您还可以使用 ClassGraph 枚举或扫描类路径中的所有类和/或所有资源(请参阅ClassGraph wiki )。

在 Java 9+ 的模块化项目中,您可能还想获取系统中可见模块的ModuleReference对象列表。 这些可以通过调用以下获得( ModuleRef是一个包装ModuleReference是向后兼容的,所以你可以在JDK 7/8编译代码,但仍利用模块上的JDK 9+功能):

List<ModuleRef> modules =
    new ClassGraph()
        .enableSystemPackages() // Optional, to return system modules
        .getModules();

或者,您可以通过调用以下命令获取传递到命令行中的实际模块路径( --module-path--patch-module--add-exports等),返回ModulePathInfo对象列表:

List<ModulePathInfo> modulePathInfo = new ClassGraph().getModulePathInfo();

为了将来参考,如果您需要将类路径传递给ProcessBuilder

StringBuffer buffer = new StringBuffer();
for (URL url :
    ((URLClassLoader) (Thread.currentThread()
        .getContextClassLoader())).getURLs()) {
  buffer.append(new File(url.getPath()));
  buffer.append(System.getProperty("path.separator"));
}
String classpath = buffer.toString();
int toIndex = classpath
    .lastIndexOf(System.getProperty("path.separator"));
classpath = classpath.substring(0, toIndex);
ProcessBuilder builder = new ProcessBuilder("java",
    "-classpath", classpath, "com.a.b.c.TestProgram");

如果其他答案不起作用,请尝试以下操作:

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
    System.out.println(url.getFile());
}

将此代码放入一个空的 jsp 页面以查看 classLoader 层次结构和在每个级别加载的关联 jar。

下面的visit()方法也可以单独使用

<%!
    public void visit(StringBuilder sb, int indent, ClassLoader classLoader) {
        if (indent > 20 || classLoader == null)
            return;
        String indentStr = new String(new char[indent]).replace("\0", "    ");
        sb.append("\n");
        sb.append(indentStr);
        sb.append(classLoader.getClass().getName());
        sb.append(":");
        if (classLoader instanceof java.net.URLClassLoader) {
            java.net.URL[] urls = ((java.net.URLClassLoader)classLoader).getURLs();
            for (java.net.URL url : urls) {
                sb.append("\n");
                sb.append(indentStr);
                sb.append(url);
            }
        }
        sb.append("\n");
        visit(sb, indent + 1, classLoader.getParent());
    }

%>

<%
StringBuilder sb = new StringBuilder();
visit(sb,1,this.getClass().getClassLoader());
%>
<pre>
<%=sb%>
</pre>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM