1

我正在开发一个命令行应用程序,该应用程序在运行时加载用户指定的文本翻译器(通过命令行 arg 提供的类文件/jar 的路径)。基本上我正在使用该参数并使用它来创建 URLClassLoader。然后我需要找到实现 Transable 接口的 URLClassloader 可用的所有类。

现在我只允许这个命令行 arg 是一个包含类文件的目录。使解决方案相当简单(下面的代码)。但老实说,我不喜欢该解决方案,因为它会分解 jar 文件、jar 文件目录等......此外,这显然会分解任何具有已定义包的类,因为 loadClass 需要包含包的全名。谁有更好的方法?

    File d = new File(path);
    if(d.isDirectory()) {
        URL url = d.toURI().toURL();
        ClassLoader cl = new URLClassLoader(new URL[]{url});

        FilenameFilter filter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".class");
            }
        };

        for(File f : d.listFiles(filter)) {
            String name = f.getName().substring(0, f.getName().indexOf("."));
            String key = "";
            if(name.endsWith("Translator")) {
                key = name.substring(0, name.indexOf("Translator"));
            }
            else if(name.endsWith("translator")) {
                key = name.substring(0, name.indexOf("translator"));
            }
            else
                key = name;

            Class c = cl.loadClass(name);
            if(Transable.class.isAssignableFrom(c)) {
                Transable t = (Transable)c.newInstance();
                env.registerTranslator(key, t);
            }
            else {
                System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
            }
        }
    }
    else {
        throw new Error("NOT IMPLEMENTED");
    }
4

5 回答 5

4

如果你继续沿着这条路走,你可能只需要蛮力。据我所知,默认的类加载器甚至不会将一个类加载到 JVM 中,除非它以某种方式被引用。这意味着,除非您知道要加载它们的完全限定类名,否则您的某些类将基本上是不可见的。

您可能需要重新考虑您的要求。因为加载一组已为其指定类名的翻译器会容易得多。

于 2011-02-17T00:11:40.283 回答
4

您应该使用 Java 的服务提供者接口 (SPI) 和ServiceLoader类(在 Java 6 中引入),而不是显式搜索实现者。SPI 几乎是一种标准方法来执行您在 Java 中描述的操作。

请参阅官方 Java 教程,了解如何创建服务提供者以及如何在运行时通过 ServiceLoader 使用它。

于 2011-02-17T02:29:04.190 回答
3

这可能符合要求。如果没有,您应该能够查看他们的来源,以了解什么对您有用。http://code.google.com/p/reflections/

于 2011-02-17T00:12:34.757 回答
3

原则上,这不适用于任意类加载器,因为它们可以使用任何可以想象的方式来实际加载类,并且根本没有任何“目录监听”功能。

类加载器甚至可以在任何时候动态生成类(即类的字节码)loadClass,并想象一个 TransableClassloader,其中每个自动定义的类都将实现您的接口——您的程序永远不会结束。


也就是说,对于URLClassloader您可以使用getURLs()jar:file:URL,您可以使用JarFileor Fileapi 来获取要尝试的文件名列表(以及类名)。由于您在 URL 中给出了包层次结构的根,因此找到正确的包名称也不困难。

(如果您需要更多详细信息,请说出来。)


编辑:对于包名称,它们对应于层次结构内的目录名称。因此,当您有一个对应于 (say) 的基本 URLdir/classes并找到一个名为 的类文件dir/classes/com/company/gui/SimpleTranslator.class时,它对应于 class com.company.gui.SimpleTranslator

因此,删除基本前缀并替换/.(并削减.class)。(在 JarFile 中,您不必删除前缀。)

实际上,如果您使用递归方法遍历您的文件层次结构,您可以使用相同的方法构建您的包名称,只需附加字符串(将它们作为参数提供给下一个递归调用):

public void searchClassesInDir(File dir, String packagePrefix) {
    if(dir.isDirectory()) {
        String prefix = packagePrefix + dir.getName() + ".";
        for(File f : dir.listFiles()) {
            searchClasses(f, prefix);
        }
    }
    else {
       String fileName = dir.getName();
       if(! fileName.endsWith(".class"))
           return;
       String className = packagePrefix + fileName.substring(0, fileName.length()-".class".length());
       // now do the rest of your processing
    }
}

searchClasses(new File(url.toURI()), "");
于 2011-02-17T00:27:09.977 回答
0

这会有帮助吗?

由于 Class c = cl.loadClass(name);

类方法 getInterfaces() 返回一个类数组

检查每个类名是否与翻译类名匹配。

于 2011-02-17T00:26:48.980 回答