Ahm*_*any 43

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

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

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

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

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

    examples.reflections.Dog
    examples.reflections.Cat
    
    Run Code Online (Sandbox Code Playgroud)
  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());
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    和注释定义:

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

    并且您必须package-info.java在该包内命名的文件中声明包级注释.这里是样本内容:

    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
    package examples.reflections;
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • 值得注意的是,Java 9 中已经将服务提供者机制集成到了模块系统中,这使得它比包级注解更加方便,即不需要创建自己的注解类型,可以在模块中声明实现- 无论如何,您在编写模块化软件时都会创建信息,并获得有关实现有效性的编译时反馈。 (2认同)

小智 28

埃里克森说的是什么,但如果你还想这样做,那么看看反思.从他们的页面:

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

  • 获取某种类型的所有子类型
  • 获取带有注释的所有类型注释
  • 获取所有注释了一些注释的类型,包括注释参数匹配
  • 得到所有用一些注释的方法

  • 更具体:`new Reflections("my.package").getSubTypesOf(MyInterface.class)` (12认同)

eri*_*son 25

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

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

所述服务提供商机构是枚举一个可插入的服务的实现中的常规手段.使用ServiceLoaderJava 6,或在早期版本中实现自己的.我在另一个答案中提供了一个例子.

  • 编写实现接口,编译和部署接口的源代码是可行的,但是包含带有实现的FQN的文本文件是不可行的?这种情况很少,如果有的话.几乎没有"经常". (4认同)
  • 不幸的是,服务提供者机制要求您在一个通常不可行的单独文件中列出感兴趣列表中的类。 (3认同)

kay*_*e99 13

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

public interface ITask {
    void doStuff();
}

@Component
public class MyTask implements ITask {
   public void doStuff(){}
}
Run Code Online (Sandbox Code Playgroud)

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

@Service
public class TaskService {

    @Autowired
    private List<ITask> tasks;
}
Run Code Online (Sandbox Code Playgroud)

  • 不完全是,Spring将填充ITask类型的所有**bean**,这不完全相同. (4认同)

Luk*_*son 8

列出实现给定接口的所有类的最强大的机制目前是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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • ClassGraph 库工作起来就像沙姆沙伊赫,谢谢@Luke Hutchison (2认同)

ver*_*lor 6

使用ClassGraph非常简单:

用于查找以下实现的 Groovy 代码my.package.MyInterface

@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
}
Run Code Online (Sandbox Code Playgroud)


Zhi*_*hao 4

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