How to get classpath from classloader?

孤者浪人 提交于 2019-11-27 00:12:13

问题


I am using some third party code which when given a '-classpath' command line argument doesnt set the java.class.path, but instead just creates a classloader, adds all the urls for the items on the command line specified classpath to the classloader, and then sets it to be the context classloader. In a plugin class to this code that I have written, I get an instance of this classloader, and somehow need to use it to get back the underlying classpath, so that I can use it in an invocation of JavaCompiler.getTask(...) and compile some other code on the fly. However there doesn't seem to be anyway to get the ClassPath from the ClassLoader, and as java.class.path is unset, I can't seem to access the underlying classpath that the application was initially invoked with...Any ideas?


回答1:


If the classloader uses URLs, it must be a URLClassloader. What you have access to is the URLs which defines the classpath for it along side with its parent ClassLoader.

To get the URLs, simply do the following:

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



回答2:


The cleanest way to enumerate the classpath today is to use the ClassGraph library (I am the author). Note that the old answer of reading the java.class.path property or calling ((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs() is woefully inadequate if you want your code to be portable today, because numerous runtime environments no longer use java.class.path, and/or their classloaders don't extend URLClassLoader, and/or they use some obscure mechanism for extending the classpath (like the Class-Path: property in a jar's manifest file), and/or your code may be run as a module in JDK 9+ (or your code will be run on the traditional classpath in JDK9+, but the standard JDK classloaders for the traditional classpath don't even extend URLClassLoader anymore).

ClassGraph handles an enormous number of classpath specification mechanisms and classloader implementations automatically. For most of the supported classloaders, custom reflection code has been written for ClassGraph to obtain the classpath from the classloader (this is required since the ClassLoader API does not have any standard mechanism for obtaining the classpath). You could write your own code for this, but probably it will only support URLClassLoader without expending significant effort -- so it is probably better to just use ClassGraph, since the work is already done for you.

To get the classpath (and non-system modular jars added to the module path), just call:

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

Note that in Java 9+, modules (or jlink'd jars) may appear in the list with jrt: URIs, which you can't do much with directly (other than use ClassGraph to read resources and classes from them, since ClassGraph can additionally use the JPMS API to access these resources and classes). You can also use ClassGraph to enumerate or scan all classes and/or all resources in the classpath (see the ClassGraph wiki).

In a modular project in Java 9+, you may also want to obtain a list of ModuleReference objects for visible modules in the system. These can be obtained by calling the following (ModuleRef is a wrapper for ModuleReference that is backwards compatible, so you can compile your code on JDK 7/8 but still take advantage of module features on JDK 9+):

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

Or you can get the actual module path passed into the commandline (--module-path, --patch-module, --add-exports etc.) by calling the following, returning a list of ModulePathInfo objects:

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



回答3:


For future reference, in case you need to pass in the class path to 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");



回答4:


In case other answers don't work, try this:

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



回答5:


Drop this code into an empty jsp page to view classLoader hierarchy and associated jars loaded at each level.

visit() method below could also be used on its own

<%!
    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>


来源:https://stackoverflow.com/questions/11613988/how-to-get-classpath-from-classloader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!