如何在Java中以编程方式获取接口的所有实现的列表?

如何在Java中以编程方式获取接口的所有实现的列表?,java,interface,Java,Interface,我可以用反射或类似的方法来完成吗?是的,第一步是确定“所有”您关心的类。如果您已经拥有这些信息,则可以枚举每个信息,并使用instanceof验证关系。这里有一篇相关文章:另外,如果您正在编写一个IDE插件(您尝试做的事情比较常见),那么IDE通常会为您提供更有效的方法来访问用户代码当前状态的类层次结构 一般来说,这样做很昂贵。要使用反射,必须加载类。如果要加载类路径上的所有可用类,则需要花费时间和内存,不建议这样做 如果要避免这种情况,您需要实现自己的类文件解析器,该解析器可以更高效地运行,而

我可以用反射或类似的方法来完成吗?

是的,第一步是确定“所有”您关心的类。如果您已经拥有这些信息,则可以枚举每个信息,并使用instanceof验证关系。这里有一篇相关文章:

另外,如果您正在编写一个IDE插件(您尝试做的事情比较常见),那么IDE通常会为您提供更有效的方法来访问用户代码当前状态的类层次结构

一般来说,这样做很昂贵。要使用反射,必须加载类。如果要加载类路径上的所有可用类,则需要花费时间和内存,不建议这样做

如果要避免这种情况,您需要实现自己的类文件解析器,该解析器可以更高效地运行,而不是反射。字节码工程库可能有助于这种方法


是枚举可插拔服务实现的常规方法,随着Java9中的项目Jigsaw(模块)的引入,这种方法变得更加成熟。在Java6中使用,或在早期版本中实现自己的。我提供了另一个答案。

埃里克森说了什么,但如果你还想这么做,那就看看。从他们的页面:

使用反射,您可以查询元数据:

  • 获取某些类型的所有子类型
  • 使用一些注释获取所有类型的注释
  • 获取使用某些注释注释的所有类型,包括注释参数匹配
  • 让所有的方法都带有一些注释

埃里克森说的最好。这里有一个相关的问答线索-

ApacheBcel库允许您在不加载类的情况下读取类。我相信它会更快,因为你应该能够跳过验证步骤。使用类加载器加载所有类的另一个问题是,您将受到巨大的内存影响,并且会无意中运行您可能不想执行的任何静态代码块


Apache BCEL库链接-

我已经搜索了一段时间,似乎有不同的方法,下面是一个总结:

  • 如果您不介意添加依赖项,则库非常流行。它看起来是这样的:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
    for (Pet implClass : loader) {
        System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
    }
    
  • 包级别注释。以下是一个例子:

    Package[] packages = Package.getPackages();
    for (Package p : packages) {
        MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
        if (annotation != null) {
            Class<?>[]  implementations = annotation.implementationsOfPet();
            for (Class<?> impl : implementations) {
                System.out.println(impl.getSimpleName());
            }
        }
    }
    
    请注意,调用
    Package.getPackages()
    ,只能加载类加载器当时已知的包


  • 此外,还有其他基于URLClassLoader的方法,这些方法将始终限于已加载的类,除非您执行基于目录的搜索

    我遇到了同样的问题。我的解决方案是使用反射来检查ObjectFactory类中的所有方法,消除那些不是返回一个绑定POJO实例的createXXX()方法的方法。这样发现的每个类都被添加到一个class[]数组中,然后该数组被传递给JAXBContext实例化调用。这执行得很好,只需要加载ObjectFactory类,这本来是需要的。我只需要维护ObjectFactory类,这是一个手工执行的任务(在我的例子中,因为我从POJO开始使用schemagen),或者可以根据需要由xjc生成。无论哪种方式,它都是性能、简单和有效的。

    Spring有一种非常简单的方法来实现这一点:

    public interface ITask {
        void doStuff();
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    
    public interface ITask {
        void doStuff();
        default ITask getImplementation() {
           return this;
        }
    
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    
    然后,您可以自动连接类型为
    ITask
    的列表,Spring将使用所有实现填充该列表:

    @Service
    public class TaskService {
    
        @Autowired
        private List<ITask> tasks;
    }
    
    @Service
    public class TaskService {
    
        @Autowired(required = false)
        private List<ITask> tasks;
    
        if ( tasks != null)
        for (ITask<?> taskImpl: tasks) {
            taskImpl.doStuff();
        }   
    }
    
    @服务
    公共类任务服务{
    @自动连线
    私有列表任务;
    }
    
    目前列出实现给定接口的所有类的最健壮的机制是,因为它处理的是,包括新的JPMS模块系统。(我是作者。)


    @kaybee99答案的新版本,但现在返回用户的问题:实现


    Spring有一个非常简单的方法来实现这一点:

    public interface ITask {
        void doStuff();
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    
    public interface ITask {
        void doStuff();
        default ITask getImplementation() {
           return this;
        }
    
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    
    然后,您可以自动连接类型为
    ITask
    的列表,Spring将使用所有实现填充该列表:

    @Service
    public class TaskService {
    
        @Autowired
        private List<ITask> tasks;
    }
    
    @Service
    public class TaskService {
    
        @Autowired(required = false)
        private List<ITask> tasks;
    
        if ( tasks != null)
        for (ITask<?> taskImpl: tasks) {
            taskImpl.doStuff();
        }   
    }
    
    @服务
    公共类任务服务{
    @自动连线(必需=错误)
    私有列表任务;
    如果(任务!=null)
    对于(ITask taskImpl:tasks){
    taskImpl.doStuff();
    }   
    }
    
    使用它非常简单:

    查找
    my.package.MyInterface
    实现的Groovy代码:

    @Grab('io.github.classgraph:classgraph:4.6.18')
    import io.github.classgraph.*
    new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
        scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
    }
    

    更具体地说:
    newreflections(“my.package”).getsubscriptsof(MyInterface.class)
    是否可以使用一些神奇的Eclipse快捷方式来实现它?请考虑选择一个答案。不幸的是,服务提供者机制要求您在一个单独的文件中列出可能在感兴趣的列表中的类,这些文件通常是不可行的。编写源代码实现接口、编译和部署它是可行的。但是在实现的FQN中包含文本文件是不可行的?这种情况很少发生。很少“经常”。JDBC的DriverManager是如何工作的?它不是在做类似的事情吗(在类路径中搜索驱动程序接口的所有实现)?@Alexemiuk否。它使用我上面描述的服务加载机制;JDBC 4.0+驱动程序必须在
    META-INF/services/java.sql.driver
    中列出其名称,这三种方法中哪一种更高效、最快?@carlspring我不能绝对说明它们的相对效率,但第一种方法(反射库)对我来说效果很好。JDBC的DriverManager是如何工作的?它不是在做类似的事情吗(在类路径中搜索驱动程序接口的所有实现)?@AlexSemeniuk我认为他们现在支持服务加载器/提供者机制(上面的方法#2),根据他们的文档,“DriverManager方法getConnection和getDrivers已经得到了增强,以支持Java标准版Service Pro