简体   繁体   English

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

[英]Java, Get all classes available to a URLClassLoader that implement a specific interface

I am working on a command line app that loads user specified text translators at runtime (path to class files/jar provided via command line arg). 我正在使用一个命令行应用程序,该应用程序在运行时加载用户指定的文本翻译器(通过命令行arg提供的类文件/ jar的路径)。 Basically I am taking that argument and using it to create a URLClassLoader. 基本上,我使用该参数并使用它来创建URLClassLoader。 Then I need to find all classes available to the URLClassloader that implement the Transable interface. 然后,我需要找到实现Transable接口的URLClassloader可用的所有类。

Right now I am only allowing this command line arg to be a directory with class files in it. 现在,我只允许此命令行arg为其中包含类文件的目录。 Making the solution fairly simple (code below). 使解决方案相当简单(下面的代码)。 But honestly I don't like the solution as it breaks down for jar files, directory of jar files, etc... Also, this obviously breaks down for any classes with a defined package, as loadClass needs the full name including the package. 但老实说,我不喜欢该解决方案,因为它无法解析jar文件,jar文件目录等。此外,对于具有定义包的任何类,这显然都无法解决,因为loadClass需要包含包的全名。 Anyone have a better method? 有人有更好的方法吗?

    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");
    }

You may just have to brute force it if you continue down this road. 如果继续这条路,您可能只需要蛮力就可以了。 To my knowledge the default class loader will not even load a class into the JVM unless it is referenced somehow. 据我所知,默认的类加载器甚至不会将类加载到JVM中,除非以某种方式引用了该类。 Which means, some of your classes will be basically invisible unless you know their fully qualified class name to load them. 这意味着,除非您知道要加载的完全合格的类名称,否则某些类基本上是不可见的。

You may want to reconsider your requirements. 您可能需要重新考虑您的要求。 As it would be far easier to load a set of Translators that you have been given the class names for. 因为加载为您提供了类名的一组翻译器要容易得多。

Instead of searching for implementors explicitly, you should uss Java's Service Provider Interface (SPI) and the ServiceLoader class (introduced in Java 6). 您应该使用Java的服务提供者接口(SPI)和ServiceLoader类(在Java 6中引入),而不是明确地搜索实现者。 SPI is a pretty much a standard way to do what you are describing in Java. SPI几乎是完成您在Java中描述的标准方法。

Please see the official Java tutorial on how to create a service provider and how to use it, at runtime, with ServiceLoader. 请参阅官方Java教程 ,了解如何创建服务提供商以及如何在运行时通过ServiceLoader使用它。

This may fit the bill. 这可能符合要求。 If not, you ought to be able to look through their source to get an idea of what will work for you. 如果没有,您应该能够浏览他们的来源,以了解适合您的方法。 http://code.google.com/p/reflections/ http://code.google.com/p/reflections/

In principle this can't work for arbitrary classloaders, as they may use any way imaginable to actually load the classes, and not have any "directory listening" function at all. 原则上,这不适用于任意的类加载器,因为它们可以使用可以想象的任何方式实际加载类,并且根本不具有任何“目录侦听”功能。

A classloader might even generate classes (ie the bytecode for the classes) on the fly whenever a loadClass comes, and imagine a TransableClassloader where each such automatically defined class would implement your interface - your program would never end. 每当loadClass出现时,类加载器甚至可能会即时生成类(即,类的字节码),并想象一个TransableClassloader,其中每个这样的自动定义的类将实现您的接口-您的程序将永远不会结束。


That said, for an URLClassloader you can use getURLs() , and for the jar: and file: URL you can use the JarFile or File api to get the list of filenames (and thus Classnames) to try. 就是说,对于URLClassloader ,可以使用getURLs() ,对于jar:file: URL,可以使用JarFileFile api获取要尝试的文件名列表(并因此获得类名)。 As you have the root of your package hierarchy given in the URL, finding the right package name is not difficult, too. 由于您具有URL中给定的包层次结构的根,因此找到正确的包名称也不难。

(If you need more details, say it.) (如果您需要更多详细信息,请说出来。)


Edit: For the package names, they correspond to the directory names inside of your hierarchy. 编辑:对于程序包名称,它们与层次结构内的目录名称相对应。 So, when you have a base URL which corresponds to (say) dir/classes , and find a class-file named dir/classes/com/company/gui/SimpleTranslator.class , it corresponds to class com.company.gui.SimpleTranslator . 因此,当您有一个对应于(例如) dir/classes的基本URL并找到一个名为dir/classes/com/company/gui/SimpleTranslator.class的类文件时,它对应于com.company.gui.SimpleTranslator类。

So, remove the base prefix and replace / by . 因此,删除基本前缀并用替换/ . (and cut of the .class ). (和.class剪切)。 (In a JarFile you don't have to cut a prefix off.) (在JarFile中,您不必删除前缀。)

Actually, if you use a recursive method to traverse your File hierarchy, you can build up your package-name with the same method, simply by appending Strings (give them as a parameter to the next recursive Invocation): 实际上,如果您使用递归方法遍历文件层次结构,则可以通过添加字符串(将它们作为参数作为下一个递归调用的附件)来使用相同的方法来构建包名:

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()), "");

Would this help? 这会有所帮助吗?

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

Class method getInterfaces() returns an array of classes 类方法getInterfaces()返回一个类数组

Check each class name for match to translator class name. 检查每个类名是否与翻译器类名匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM