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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/347248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:41:25  来源:igfitidea点击:

How can I get a list of all the implementations of an interface programmatically in Java?

javainterface

提问by user2427

Can I do it with reflection or something like that?

我可以用反射或类似的东西来做吗?

回答by Zhichao

Yes, the first step is to identify "all" the classes that you cared about. If you already have this information, you can enumerate through each of them and use instanceof to validate the relationship. A related article is here: http://www.javaworld.com/javaworld/javatips/jw-javatip113.html

是的,第一步是确定您关心的“所有”类。如果您已经拥有这些信息,您可以枚举它们中的每一个并使用 instanceof 来验证关系。相关文章在这里:http: //www.javaworld.com/javaworld/javatips/jw-javatip113.html

回答by Uri

Also, if you are writing an IDE plugin (where what you are trying to do is relatively common), then the IDE typically offers you more efficient ways to access the class hierarchy of the current state of the user code.

此外,如果您正在编写 IDE 插件(您尝试执行的操作相对常见),那么 IDE 通常会为您提供更有效的方法来访问用户代码当前状态的类层次结构。

回答by erickson

In general, it's expensive to do this. To use reflection, the class has to be loaded. If you want to load every class available on the classpath, that will take time and memory, and isn't recommended.

一般来说,这样做很昂贵。要使用反射,必须加载类。如果您想加载类路径上可用的每个类,这将花费时间和内存,并且不建议这样做。

If you want to avoid this, you'd need to implement your own class file parser that operated more efficiently, instead of reflection. A byte code engineering library may help with this approach.

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

The Service Provider mechanismis the conventional means to enumerate implementations of a pluggable service, and has become more established with the introduction of Project Jigsaw (modules) in Java 9. Use the ServiceLoaderin Java 6, or implement your own in earlier versions. I provided an examplein another answer.

服务提供机制是枚举可插拔服务过程中的常规手段,并已成为更成熟的在Java 9.使用引进Jigsaw项目(模块)的ServiceLoaderJava 6中,或实现自己的早期版本。我在另一个答案中提供了一个例子

回答by Peter Severin

What erickson said, but if you still want to do it then take a look at Reflections. From their page:

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

Using Reflections you can query your metadata for:

  • get all subtypes of some type
  • get all types annotated with some annotation
  • get all types annotated with some annotation, including annotation parameters matching
  • get all methods annotated with some

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

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

回答by Peter Severin

What erikson said is best. Here's a related question and answer thread - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

埃里克森说的最好。这是一个相关的问答线程 - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

The Apache BCEL library allows you to read classes without loading them. I believe it will be faster because you should be able to skip the verification step. The other problem with loading all classes using the classloader is that you will suffer a huge memory impact as well as inadvertently run any static code blocks which you probably do not want to do.

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

The Apache BCEL library link - http://jakarta.apache.org/bcel/

Apache BCEL 库链接 - http://jakarta.apache.org/bcel/

回答by Ahmad Abdelghany

I have been searching for a while and there seems to be different approaches, here is a summary:

我一直在寻找一段时间,似乎有不同的方法,这里是一个总结:

  1. reflectionslibrary is pretty popular if u don't mind adding the dependency. It would look like this:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
  2. ServiceLoader(as per erickson answer) and it would look like this:

    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
    for (Pet implClass : loader) {
        System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
    }
    

    Note that for this to work you need to define Petas a ServiceProviderInterface (SPI) and declare its implementations. you do that by creating a file in resources/META-INF/serviceswith the name examples.reflections.Petand declare all implementations of Petin it

    examples.reflections.Dog
    examples.reflections.Cat
    
  3. package-level annotation. here is an example:

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

    and the annotation definition:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PACKAGE)
    public @interface MyPackageAnnotation {
        Class<?>[] implementationsOfPet() default {};
    }
    

    and you must declare the package-level annotation in a file named package-info.javainside that package. here are sample contents:

    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
    package examples.reflections;
    

    Note that only packages that are known to the ClassLoader at that time will be loaded by a call to Package.getPackages().

  1. 如果您不介意添加依赖项,则反射库非常受欢迎。它看起来像这样:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
  2. ServiceLoader(根据 erickson 的回答),它看起来像这样:

    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
    for (Pet implClass : loader) {
        System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
    }
    

    请注意,要使其工作,您需要定义Pet为 ServiceProviderInterface (SPI) 并声明其实现。您可以通过在resources/META-INF/services其中创建一个具有名称的文件并在其中examples.reflections.Pet声明 的所有实现来Pet做到这一点

    examples.reflections.Dog
    examples.reflections.Cat
    
  3. 包级注释。这是一个例子:

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

    和注释定义:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PACKAGE)
    public @interface MyPackageAnnotation {
        Class<?>[] implementationsOfPet() default {};
    }
    

    并且您必须package-info.java在该包内命名的文件中声明包级注释。以下是示例内容:

    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
    package examples.reflections;
    

    请注意,只有当时 ClassLoader 已知的包才会通过调用Package.getPackages().

In addition, there are other approaches based on URLClassLoader that will always be limited to classes that have been already loaded, Unless you do a directory-based search.

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

回答by NDK

I ran into the same issue. My solution was to use reflection to examine all of the methods in an ObjectFactory class, eliminating those that were not createXXX() methods returning an instance of one of my bound POJOs. Each class so discovered is added to a Class[] array, which was then passed to the JAXBContext instantiation call. This performs well, needing only to load the ObjectFactory class, which was about to be needed anyway. I only need to maintain the ObjectFactory class, a task either performed by hand (in my case, because I started with POJOs and used schemagen), or can be generated as needed by xjc. Either way, it is performant, simple, and effective.

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

回答by kaybee99

Spring has a pretty simple way to acheive this:

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

public interface ITask {
    void doStuff();
}

@Component
public class MyTask implements ITask {
   public void doStuff(){}
}

Then you can autowire a list of type ITaskand Spring will populate it with all implementations:

然后你可以自动装配一个类型列表,ITaskSpring 会用所有的实现来填充它:

@Service
public class TaskService {

    @Autowired
    private List<ITask> tasks;
}

回答by Luke Hutchison

The most robust mechanism for listing all classes that implement a given interface is currently ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author.)

列出实现给定接口的所有类的最健壮的机制目前是ClassGraph,因为它处理最广泛的类路径规范机制,包括新的 JPMS 模块系统。(我是作者。)

try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
        .enableClassInfo().scan()) {
    for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
        foundImplementingClass(ci);  // Do something with the ClassInfo object
    }
}

回答by Please_Dont_Bully_Me_SO_Lords

A new version of @kaybee99's answer, but now returning what the user asks: the implementations...

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

Spring has a pretty simple way to acheive this:

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

public interface ITask {
    void doStuff();
    default ITask getImplementation() {
       return this;
    }

}

@Component
public class MyTask implements ITask {
   public void doStuff(){}
}

Then you can autowire a list of type ITaskand Spring will populate it with all implementations:

然后你可以自动装配一个类型列表,ITaskSpring 会用所有的实现来填充它:

@Service
public class TaskService {

    @Autowired(required = false)
    private List<ITask> tasks;

    if ( tasks != null)
    for (ITask<?> taskImpl: tasks) {
        taskImpl.doStuff();
    }   
}