查找 Java 包中的所有类 您所在的位置:网站首页 java获取某个包中所有类 查找 Java 包中的所有类

查找 Java 包中的所有类

2024-07-11 04:34| 来源: 网络整理| 查看: 265

转自:https://www.baeldung.com/java-find-all-classes-in-package

1. 概述

有时,我们想获取有关应用程序运行时的行为信息,例如查找运行时可用的所有类。

在本教程中,我们将探讨如何在运行时查找 Java 包中的所有类的几个示例。

2. 类加载器

首先,我们将从 Java 类加载器开始讨论。 Java 类加载器是 Java 运行时环境 (JRE: Java Runtime Environment) 的一部分,可将 Java 类动态加载到 Java 虚拟机 (JVM) 中。 Java 类加载器将 JRE 与文件和文件系统细节分离。 并非所有类都由单个类加载器加载。

让我们通过图形表示来了解 Java 中可用的类加载器:

ClassLoaders

Java 9 对类加载器进行了一些重大更改。 随着模块的引入,我们可以选择在提供类路径的同时提供模块路径。 系统类加载器加载模块路径上的类。

类加载器是动态的。 他们不需要告诉 JVM 它可以在运行时提供哪些类。 因此,在包中查找类本质上是一种文件系统操作,而不是使用 Java 反射完成的操作。

但是,我们可以编写自己的类加载器或检查类路径以查找包中的类。

3. 在 Java 包中查找类

为了我们的说明,让我们创建一个包 com.baeldung.reflection.access.packages.search。

现在,让我们定义一个示例类:

1234public class ClassExample { class NestedClass { }}

接下来,让我们定义一个接口:

12public interface InterfaceExample {}

在下一节中,我们将了解如何使用系统类加载器和一些第三方库来查找类。

3.1 系统类加载器

首先,我们将使用内置的系统类加载器。 系统类加载器加载在类路径中找到的所有类。 这发生在 JVM 的早期初始化过程中:

12345678910111213141516171819202122public class AccessingAllClassesInPackage { public Set findAllClassesUsingClassLoader(String packageName) { InputStream stream = ClassLoader.getSystemClassLoader() .getResourceAsStream(packageName.replaceAll("[.]", "/")); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); return reader.lines() .filter(line -> line.endsWith(".class")) .map(line -> getClass(line, packageName)) .collect(Collectors.toSet()); } private Class getClass(String className, String packageName) { try { return Class.forName(packageName + "." + className.substring(0, className.lastIndexOf('.'))); } catch (ClassNotFoundException e) { // handle the exception } return null; }}

在上面的示例中,我们使用静态 getSystemClassLoader() 方法加载系统类加载器。

接下来,我们将在给定包中找到资源。 我们将使用 getResourceAsStream 方法将资源作为 URL 流读取。 要获取包下的资源,我们需要将包名转换为 URL 字符串。 因此,我们必须将所有点 (.) 替换为路径分隔符 (“/”)。

之后,我们将把我们的流输入到 BufferedReader 并过滤所有带有 .class 扩展名的 URL。 获得所需资源后,我们将构建类并将所有结果收集到一个 Set 中。 由于 Java 不允许 lambda 抛出异常,我们必须在 getClass 方法中处理它。

现在让我们测试这个方法:

123456789@Testpublic void when_findAllClassesUsingClassLoader_thenSuccess() { AccessingAllClassesInPackage instance = new AccessingAllClassesInPackage(); Set classes = instance.findAllClassesUsingClassLoader( "com.baeldung.reflection.access.packages.search"); Assertions.assertEquals(3, classes.size());}

包中只有两个 Java 文件。 但是,我们声明了三个类——包括嵌套类 NestedExample。 结果,我们的测试产生了三个类。

请注意,搜索包与当前工作包不同。

3.2 Reflections 库

Reflections 是一个流行的库,它扫描当前的类路径并允许我们在运行时查询它。

让我们首先将Reflections依赖项添加到我们的 Maven 项目中:

12345 org.reflections reflections 0.9.12

现在,让我们深入研究代码示例:

123456public Set findAllClassesUsingReflectionsLibrary(String packageName) { Reflections reflections = new Reflections(packageName, new SubTypesScanner(false)); return reflections.getSubTypesOf(Object.class) .stream() .collect(Collectors.toSet());}

在此方法中,我们将初始化 SubTypesScanner 类并获取 Object 类的所有子类型。 通过这种方法,我们在获取类时获得了更多的粒度。

再次,让我们测试一下:

123456789@Testpublic void when_findAllClassesUsingReflectionsLibrary_thenSuccess() { AccessingAllClassesInPackage instance = new AccessingAllClassesInPackage(); Set classes = instance.findAllClassesUsingReflectionsLibrary( "com.baeldung.reflection.access.packages.search"); Assertions.assertEquals(3, classes.size());}

与我们之前的测试类似,此测试查找给定包中声明的类。

现在,让我们继续下一个示例。

3.3 Google Guava 库

在本节中,我们将了解如何使用 Google Guava 库查找类。 Google Guava 提供了一个 ClassPath 实用程序类,它可以扫描类加载器的源并找到所有可加载的类和资源。

首先,让我们将 guava 依赖添加到我们的项目中:

12345 com.google.guava guava 31.0.1-jre

让我们深入研究代码:

123456789public Set findAllClassesUsingGoogleGuice(String packageName) throws IOException { return ClassPath.from(ClassLoader.getSystemClassLoader()) .getAllClasses() .stream() .filter(clazz -> clazz.getPackageName() .equalsIgnoreCase(packageName)) .map(clazz -> clazz.load()) .collect(Collectors.toSet());}

在上述方法中,我们提供系统类加载器作为 ClassPath#from 方法的输入。 ClassPath 扫描到的所有类都会根据包名进行过滤。 然后加载过滤的类(但不链接或初始化)并收集到一个集合中。

现在让我们测试这个方法:

123456789@Testpublic void when_findAllClassesUsingGoogleGuice_thenSuccess() throws IOException { AccessingAllClassesInPackage instance = new AccessingAllClassesInPackage(); Set classes = instance.findAllClassesUsingGoogleGuice( "com.baeldung.reflection.access.packages.search"); Assertions.assertEquals(3, classes.size());}

此外,Google Guava 库提供了 getTopLevelClasses() 和 getTopLevelClassesRecursive() 方法。

需要注意的是,在上述所有示例中,package-info 包含在可用类列表中(如果存在于包下并使用一个或多个包级注释进行注释)。

下一节将讨论如何在模块化应用程序中查找类。

4. 在模块化应用程序中查找类

Java 平台模块系统(JPMS: Java Platform Module System) 向我们介绍了新的访问控制级别:模块。每个包都必须显式导出才能在模块外访问。

在模块化应用程序中,每个模块可以是命名、未命名或自动模块之一。

对于命名和自动模块,内置的系统类加载器将没有类路径。系统类加载器将使用应用程序模块路径搜索类和资源。

对于未命名的模块,它将类路径设置为当前工作目录。

4.1. 在一个模块内

一个模块中的所有包都对模块中的其他包可见。模块内的代码可以反射访问所有类型及其所有成员。

4.2.模块外

由于 Java 强制执行最严格的访问,我们必须使用 export 或 open 模块声明显式声明包,以获得对模块内类的反射访问。

对于普通模块,导出包(但不是打开包)的反射访问仅提供对public和protected类型及其声明包的所有成员的访问。

我们可以构造一个模块,导出需要搜索的包:

123module my.module { exports com.baeldung.reflection.access.packages.search;}

对于普通模块,打开包的反射访问提供对声明包的所有类型及其成员的访问:

123module my.module { opens com.baeldung.reflection.access.packages.search;}

同样,开放模块授予对所有类型及其成员的反射访问权限,就好像所有包都已打开一样。 现在让我们打开我们的整个模块进行反射访问:

12open module my.module{}

最后,在确保为模块提供了用于访问包的正确模块化描述之后,上一节中的任何方法都可用于查找包内的所有可用类。

5. 结论

总之,我们了解了类加载器以及在包中查找所有类的不同方法。 此外,我们还讨论了在模块化应用程序中访问包。

像往常一样,所有代码都可以在 GitHub 上找到。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有