如何从classloader获取classpath?

时间:2012-07-23 13:56:47

标签: java classloader contextclassloader

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

5 个答案:

答案 0 :(得分:55)

如果类加载器使用URL,则必须是URLClassloader。您有权访问的是为其定义类路径的URL及其父ClassLoader

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

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

答案 1 :(得分:16)

更新:我的原始答案严重不足,现在我已经花了三年时间开发FastClasspathScanner,并且已经提交了大量关于某些不使用此库的类路径环境的错误报告。 FastClasspathScanner now handles numerous complex classpath specification mechanisms。即使只是找到类路径也可能在一般情况下非常复杂(更不用说扫描了它),因为有很多方法可以将jar和目录添加到类路径中。

首先,我在下面给出的代码只处理URLClassLoader,很多主要的运行时环境和容器都没有扩展它,他们从头开始实现自己的类加载器。但是在Java 9+的情况下它变得比这复杂得多,因为即使传统的类路径仍然存在,未来的一切都将朝着使用模块路径而不是类路径的方向发展。模块有网址,但它们是"jrt:/"网址,而不是"file:/"网址,模块网址实际上并不包含文件路径,只包含模块名称 - 因此您甚至无法访问通常将模块放在磁盘上。您唯一的选择是使用(重度封装的)模块系统来处理模块。 I wrote about how to scan the module path here

FastClasspathScanner处理许多复杂的类路径规范机制,因此您不需要重新发明轮子。您可以fetch a list of classpath entries from FastClasspathScanner - 这将为您省去尝试使用您在野外发现的所有不同类路径规范机制的麻烦。 (如果最后一个链接中断,则道歉 - FCS的API和文档很快就会改变。)

-

[旧答案 - 已废弃:]

其他答案在大多数情况下都是正确的,但它比某些设置更复杂:例如: MavenTomcat和JUnit都有自己的类路径支持,他们不使用系统类路径。

到目前为止,这是我设法提出的最完整的系统(此代码来自我的Fast Classpath Scanner项目):

/** The unique elements of the classpath, as an ordered list. */
private final ArrayList<File> classpathElements = new ArrayList<>();

/** The unique elements of the classpath, as a set. */
private final HashSet<String> classpathElementsSet = new HashSet<>();

/** Clear the classpath. */
private void clearClasspath() {
    classpathElements.clear();
    classpathElementsSet.clear();
}

/** Add a classpath element. */
private void addClasspathElement(String pathElement) {
    if (classpathElementsSet.add(pathElement)) {
        final File file = new File(pathElement);
        if (file.exists()) {
            classpathElements.add(file);
        }
    }
}

/** Parse the system classpath. */
private void parseSystemClasspath() {
    // Look for all unique classloaders.
    // Keep them in an order that (hopefully) reflects the order in which class resolution occurs.
    ArrayList<ClassLoader> classLoaders = new ArrayList<>();
    HashSet<ClassLoader> classLoadersSet = new HashSet<>();
    classLoadersSet.add(ClassLoader.getSystemClassLoader());
    classLoaders.add(ClassLoader.getSystemClassLoader());
    if (classLoadersSet.add(Thread.currentThread().getContextClassLoader())) {
        classLoaders.add(Thread.currentThread().getContextClassLoader());
    }
    // Dirty method for looking for any other classloaders on the call stack
    try {
        // Generate stacktrace
        throw new Exception();
    } catch (Exception e) {
        StackTraceElement[] stacktrace = e.getStackTrace();
        for (StackTraceElement elt : stacktrace) {
            try {
                ClassLoader cl = Class.forName(elt.getClassName()).getClassLoader();
                if (classLoadersSet.add(cl)) {
                    classLoaders.add(cl);
                }
            } catch (ClassNotFoundException e1) {
            }
        }
    }

    // Get file paths for URLs of each classloader.
    clearClasspath();
    for (ClassLoader cl : classLoaders) {
        if (cl != null) {
            for (URL url : ((URLClassLoader) cl).getURLs()) {
                if ("file".equals(url.getProtocol())) {
                    addClasspathElement(url.getFile());
                }
            }
        }
    }
}

/** Override the system classpath with a custom classpath to search. */
public FastClasspathScanner overrideClasspath(String classpath) {
    clearClasspath();
    for (String pathElement : classpath.split(File.pathSeparator)) {
        addClasspathElement(pathElement);
    }
    return this;
}

/**
 * Get a list of unique elements on the classpath (directories and files) as File objects, preserving order.
 * Classpath elements that do not exist are not included in the list.
 */
public ArrayList<File> getUniqueClasspathElements() {
    return classpathElements;
}

答案 2 :(得分:7)

以备将来参考,以防您需要将类路径传递给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");

答案 3 :(得分:5)

如果其他答案无效,请尝试:

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

答案 4 :(得分:0)

将此代码放入一个空的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>