【问题标题】:Can a directory be added to the class path at runtime?可以在运行时将目录添加到类路径中吗?
【发布时间】:2011-10-25 03:32:01
【问题描述】:

为了更好地理解 Java 中的工作原理,我想知道是否可以在运行时动态地将目录添加到类路径中。

例如,如果我使用 "java -jar mycp.jar" 启动 .jar 并输出 java.class.path财产,我可能会得到:

java.class.path: '.:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java'

现在我可以在运行时修改这个类路径来添加另一个目录吗? (例如,在使用位于我要添加的目录中的 .jar 对类进行第一次调用之前)。

【问题讨论】:

    标签: java classpath


    【解决方案1】:

    您可以使用以下方法:

    URLClassLoader.addURL(URL url)
    

    但您需要通过反射来执行此操作,因为方法是 protected:

    public static void addPath(String s) throws Exception {
        File f = new File(s);
        URL u = f.toURL();
        URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[]{u});
    }
    

    请参阅Reflection 上的 Java Trail。尤其是反射的缺点

    部分

    【讨论】:

    • 这对我不起作用.. 在此之后我无法使用 Class.forName("com.mysql.jdbc.Driver"); 加载类;
    • addURL 不公开或不公开是有原因的。这是一种不必要的暴力方法,不应在生产代码中使用。 Java 设计人员提供了不止一种方法来满足需要。
    • 似乎对 URLClassLoader 的强制转换在 Java 9 中被破坏了(community.oracle.com/thread/4011800 和其他人)
    【解决方案2】:

    2014 年更新:这是 Jonathan Spooner 从 2011 年开始接受的答案中的代码,略微重写以使 Eclipse 的验证器不再创建警告(弃用、原始类型)

    //need to do add path to Classpath with reflection since the URLClassLoader.addURL(URL url) method is protected:
    public static void addPath(String s) throws Exception {
        File f = new File(s);
        URI u = f.toURI();
        URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class<URLClassLoader> urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[]{u.toURL()});
    }
    

    【讨论】:

    • 我认为,编辑接受的答案会更加stackoverflowish。不再有重复的答案,这就是我们在这个网站上喜欢的。
    • @Zeemee 我几乎不记得了,也许我认为这是一个边缘案例。我只是决定过多地避免接受的答案;我不想超出作者的意图。
    【解决方案3】:

    是的,您可以使用URLClassLoader.. 参见示例here。不使用反射。

    -- 编辑--

    按照建议从链接中复制示例。

    import javax.naming.*;
    import java.util.Hashtable;
    import java.net.URLClassLoader;
    import java.net.URL;
    import java.net.MalformedURLException;
    
    public class ChangeLoader {
    
        public static void main(String[] args) throws MalformedURLException {
        if (args.length != 1) {
            System.err.println("usage: java ChangeLoader codebase_url");
            System.exit(-1);
        }
    
        String url = args[0];
        ClassLoader prevCl = Thread.currentThread().getContextClassLoader();
    
        // Create class loader using given codebase
        // Use prevCl as parent to maintain current visibility
        ClassLoader urlCl = URLClassLoader.newInstance(new URL[]{new URL(url)}, prevCl);
    
            try {
            // Save class loader so that we can restore later
                Thread.currentThread().setContextClassLoader(urlCl);
    
            // Expect that environment properties are in
            // application resource file found at "url"
            Context ctx = new InitialContext();
    
            System.out.println(ctx.lookup("tutorial/report.txt"));
    
            // Close context when no longer needed
            ctx.close();
        } catch (NamingException e) {
            e.printStackTrace();
            } finally {
                // Restore
                Thread.currentThread().setContextClassLoader(prevCl);
            }
        }
    }
    

    【讨论】:

    • 那里的例子很好,他们避免使用反射。但也许您可以在此处复制它们 - 以防链接损坏?
    • 这很好,可以避免反射,但它是有代价的:您不是向进程类路径添加路径,而是创建一个新的类加载器,其中包含新路径并在具体线程中使用它,在这种情况下,您当前的线程。这意味着以这种方式添加的任何路径都只会在那些具有显式修改的类加载器的线程上被记录。
    • @PabloFranciscoPérezHidalgo 这当然是应该使用的方式 - 你的意思是嵌套你的类加载器,所以它们有一个定义的范围(你在应用程序服务器、osgi 等中所做的),而不是改变你的系统类加载器。
    • @eis 当然,但是 OP 很好奇是否可以将路径添加到当前进程类路径。
    • @PabloFranciscoPérezHidalgo 我认为这不是 OP 的意图。无论如何,我不太同意反射的蛮力方法,尤其是当它不需要时,因为 Java 人提供了方法来做到这一点。
    猜你喜欢
    • 1970-01-01
    • 2010-11-22
    • 2016-06-03
    • 2020-01-23
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2022-01-18
    相关资源
    最近更新 更多