【问题标题】:Dynamically load top-level class in a jar outside of classpath在类路径之外的 jar 中动态加载顶级类
【发布时间】:2015-09-10 00:50:17
【问题描述】:

更新 - 我的项目中的所有文件现在都声明了同一个包。但是,我正在加载的 jar 必须在类路径之外。我试过child.loadClass(className)Class.forName(className, false, child);,但他们似乎都找不到。

我正在编写一个程序,它希望以 jar 的形式获取插件或模块,并像嵌入到类路径中一样使用它们。

我在这里和其他网站上看到了很多类似的问题,因此我为重复表示歉意。但是,到目前为止,我看到的每个答案都没有奏效。我做错了吗?

我有一个装满罐子的文件夹。我知道每个 jar 都有一个扩展我的 Module 类的顶级类。但是,我不知道这些类叫什么。

首先,我找到目录中的所有文件。这很好用。然后,对于每个文件,我尝试使用以下代码获取其顶级类的实例:

(首先它以 zip 的形式打开 jar 以获取所有类的名称,然后查看名称以判断它是否是顶级类,如果是,则应该加载、实例化并返回它.)

private static Module jarLoader(String jar){
    try{
        ZipInputStream zip = new ZipInputStream(new FileInputStream(ModulesDir+"/"+jar));
        URL[] myJarFile = new URL[]{new URL("jar","","file:"+ModulesDir)};
        URLClassLoader child = new URLClassLoader (myJarFile , Main.class.getClassLoader());
        for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
            if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
                // This ZipEntry represents a class. Now, what class does it represent?
                String className = entry.getName().replace('/', '.'); // including ".class"
                className = className.substring(0, className.length() - ".class".length());


                String[] classNameParts = className.replace('$', ':').split(":");//I don't know why i have to do this, but it won't split on "$"
                if(classNameParts.length == 1){
                    System.out.println("trying to load "+className);
                    Class classToLoad = Class.forName(className, false, child);
                    System.out.println("loaded class "+classToLoad.getName());
                    if(classToLoad.getSuperclass() == Module.class){
                        Module instance = (Module) classToLoad.newInstance();
                        return instance;
                    }
                }
            }
        }
        zip.close();
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

它达到了“试图加载...”,它显示了我所期望的类的名称,然后抛出一个java.lang.ClassNotFoundException

当我将它放在源目录中的 .java 文件中时,使用相同的名称加载类工作正常,但它不会从 jar 中获取它。

我做错了吗?我怀疑类名可能需要某种前缀或后缀,但我不知道是什么。

感谢您的帮助!

【问题讨论】:

  • 类名也需要它的完整包名
  • @JunedAhsan 我猜到了,但我不知道要使用什么包名,因为我没有在 jar 中指定一个。在每个 jar 中声明一个包是否有默认值或我唯一的选择?
  • 你把所有的罐子都放在你的类路径下了吗? Class.forName() 将搜索当前类路径,即在运行 'java.exe' 时使用 '-classpath' 参数指定的路径

标签: java jar classloader dynamically-generated dynamic-class-loaders


【解决方案1】:

throw ClassNotFoundException是你的加载jar文件没有添加到类路径中,下面的代码没有成功加载你的jar文件。

URL[] myJarFile = new URL[]{new URL("jar","","file:"+ModulesDir)};
        URLClassLoader child = new URLClassLoader (myJarFile , Main.class.getClassLoader());

应该是这样的:

   URL[] myJarFile = new URL[]{new URL("jar","",jarFilePath)};

   URLClassLoader child = new URLClassLoader (myJarFile, this.getClass().getClassLoader());

javac编译类时,内部类会以全路径表示,用$分割,如com/hello/world/TT$InnerClass.class

完整样本:

 private void jarLoader(){
        try{
            String ModulesDir = "./test/test.jar";
            ZipInputStream zip = new ZipInputStream(new FileInputStream(ModulesDir));

            URL[] myJarFile = new URL[]{new URL("jar","",ModulesDir)};

            URLClassLoader child = new URLClassLoader (myJarFile , this.getClass().getClassLoader());

            for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
                if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
                    // This ZipEntry represents a class. Now, what class does it represent?
                    String className = entry.getName().replace('/', '.'); // including ".class"
                    className = className.substring(0, className.length() - ".class".length());


                    String[] classNameParts = className.replace('$', ':').split(":");//I don't know why i have to do this, but it won't split on "$"
                    if(classNameParts.length == 1){
                        System.out.println("trying to load "+className);
                        Class classToLoad = Class.forName(className, true, child);
                        System.out.println("loaded class "+classToLoad.getName());
                    }
                }
            }
            zip.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    } 

【讨论】:

    【解决方案2】:

    所有建议似乎都不起作用,因为我需要加载的文件在我的类路径之外。

    This answer 可以工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-16
      相关资源
      最近更新 更多