【问题标题】:Dynamic Jar loading is confusing me动态 Jar 加载让我感到困惑
【发布时间】:2012-06-16 01:57:17
【问题描述】:

从概念上讲,当我加载一个新 Jar 时我在做什么? URLClassloader 是唯一的选择吗?我应该如何形成这些 URL 以指向包含更多 jar 的子目录。

如果有人觉得超级慷慨,那么一些执行以下操作的演示代码将非常有帮助(假设“jars/A.jar”包含我们要实例化的“myClass”):

  1. 从子目录加载一些 jar
  2. 从中返回一个特定的类
  3. 实例化该类

【问题讨论】:

    标签: java classloader


    【解决方案1】:

    从概念上讲,当我加载一个新 Jar 时我在做什么?

    从概念上讲,您不是在加载新的 JAR。相反,您正在定义一个类加载器,它将按需从 JAR 文件中加载代码和其他资源。

    URLClassloader 是唯一的选择吗?

    理论上,您可以实现自己的 ClassLoader 子类,但这对于您想要做的事情来说不是必需的。

    我应该如何形成这些 URL 以指向包含更多 jar 的子目录。

    我认为这是您问题的关键。 URLClassLoader 构造函数解释 URL[] 参数如下:

    “任何以 '/' 结尾的 URL 都假定指向一个目录。否则,假定 URL 指向一个 JAR 文件,该文件将根据需要下载和打开。”

    在上面引用文本的第一种情况下,目录被假定为包含要加载的资源的树的根。

    但我猜您正在尝试设置一个类加载器,该类加载器将从给定目录中的所有 JAR 文件加载。为此,您需要:

    1. 读取目录并为任何 JAR 文件构建 File 对象列表。
    2. 创建一个数组来保存相同数量的URL 实例。
    3. 对于每个 JAR 文件 File,使用 File.toURL() 创建一个 URL 并添加到数组中。 (使用 File.toURL() 意味着您将获得适用于您的平台的 JAR 文件的 kosher "file:" URL。)
    4. 使用URL 数组创建URLClassLoader

    【讨论】:

      【解决方案2】:

      非常短的版本:当您加载新的 JAR 时,您为 Java 提供了一个寻找类的新位置。

      为了更详细的解释,维基百科有一篇与此相关的文章:http://en.wikipedia.org/wiki/Java_Classloader

      另外,不妨试试:http://onjava.com/pub/a/onjava/2005/01/26/classloading.html

      URLClassLoader 不是您唯一的选择;查看http://download-llnw.oracle.com/javase/tutorial/deployment/jar/apiindex.html

      Re:如何形成 URL,来自 Java 文档 (http://download-llnw.oracle.com/javase/7/docs/api/java/net/URLClassLoader.html):

      此类加载器用于从引用 JAR 文件和目录的 URL 的搜索路径加载类和资源。假设任何以“/”结尾的 URL 都指向一个目录。否则,假定 URL 引用将根据需要打开的 JAR 文件。

      改编自How should I load Jars dynamically at runtime?

      URLClassLoader child = new URLClassLoader (new URL("jars/A.jar"), this.getClass().getClassLoader());
      Class classToLoad = Class.forName ("com.myClass", true, child);
      Method method = classToLoad.getDeclaredMethod ("myMethod");
      Object instance = classToLoad.newInstance ();
      Object result = method.invoke (instance);
      

      此外,上面链接的答案列出了 URLClassLoader 的更多替代方案(他们提到了 OSGi、JCL 和其他一些,我没有任何经验,或者我会在这里说些什么)。

      HTH

      【讨论】:

        【解决方案3】:

        如果知道 jar 列表,那么一个可能更好/更简单的解决方案就是向父 jar 的清单添加一个 Class-Path 条目。这样您就可以完全避免使用自定义类加载器。

        Class-Path: jars/A.jar
        

        显然,在构建时不知道依赖 jar 的情况下,这将不起作用。

        【讨论】:

        • “动态”加载的具体情况如何?
        【解决方案4】:

        简单的方法是在启动程序之前确保依赖的 JAR 在类路径中。


        如果你想从一个子目录加载,你需要确定JAR的路径给URLClassLoader。你可以使用这样的东西来获取你的程序的路径:

        new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath())
        

        这有点像 hack,但我不知道有任何官方方法可以获取 Java 程序的路径。


        如果您想动态加载 JAR 以便可以像在类路径中指定它一样使用它,请查看 thread。它绝对属于不受支持的 hack 领域,但它也很简单。

        我使用的修改版:

        import java.io.File;
        import java.lang.reflect.Method;
        import java.net.MalformedURLException;
        import java.net.URL;
        import java.net.URLClassLoader;
        
        /**
         * Hack to modify classpath at runtime. Can be used to load JARs as if they were
         * loaded from the start.
         * 
         * Warning: May break if URLClassLoader changes
         * 
         * @author Antony Miguel
         */
        public class ClasspathHacker {
            /**
             * Add a file to the classpath.
             * 
             * @param pPath
             *            path to the file
             */
            public static void addFile(String pPath) {
                File f = new File(pPath);
                addFile(f);
            }
        
            /**
             * Add a file to the classpath.
             * 
             * @param pFile
             *            the file to be added
             */
            public static void addFile(File pFile) {
                try {
                    addURL(pFile.toURI().toURL());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        
            /**
             * Add the content pointed to by a URL to the classpath.
             * 
             * @param pURL
             *            the URL pointing to the content to be added
             */
            public static void addURL(URL pURL) {
                /*
                 * Use reflection to call addURL on the system classloader, which we
                 * expect to be a URLClassLoader
                 */
                Method method;
                try {
                    method = URLClassLoader.class.getDeclaredMethod("addURL",
                            new Class[] { URL.class });
                    method.setAccessible(true);
                    method.invoke(ClassLoader.getSystemClassLoader(),
                            new Object[] { pURL });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-10-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-05
          相关资源
          最近更新 更多