Java,获取实现特定接口的URLClassLoader可用的所有类

时间:2011-02-17 00:03:39

标签: java reflection classloader serviceloader

我正在开发一个命令行应用程序,它在运行时加载用户指定的文本转换器(通过命令行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");
    }

5 个答案:

答案 0 :(得分:4)

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

您可能需要重新考虑您的要求。因为加载一组Translators会更容易,因为你已经为它们提供了类名。

答案 1 :(得分:4)

不应该显式地搜索实现者,而应该使用Java的服务提供者接口(SPI)和ServiceLoader类(在Java 6中引入)。 SPI是用Java完成的一种标准方法。

请参阅official Java tutorial,了解如何在运行时使用ServiceLoader创建服务提供程序以及如何使用它。

答案 2 :(得分:3)

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

答案 3 :(得分:3)

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

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


也就是说,对于URLClassloader,您可以使用getURLs(),对于jar:file:网址,您可以使用JarFile或{{1 api获取要尝试的文件名列表(以及类名)。由于您拥有URL中给出的包层次结构的根,因此找到正确的包名称并不困难。

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


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

因此,删除基本前缀并将com.company.gui.SimpleTranslator替换为/(以及.的剪切)。 (在JarFile中,您不必删除前缀。)

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

.class

答案 4 :(得分:0)

这会有帮助吗?

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

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

检查每个班级名称是否与译员班级名称匹配。