1. 简介

Reflections 是一个强大的类路径扫描器。它会扫描并索引元数据,支持在运行时进行查询。此外,还可以将扫描结果保存下来,这样在整个项目生命周期中可以随时复用这些信息,而无需重复扫描类路径。

本文将带你了解如何在 Java 项目中配置和使用 Reflections 库。

2. Maven 依赖

要使用 Reflections,首先需要添加其 Maven 依赖:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

你可以在 Maven Central 找到最新的版本。

3. 配置 Reflections

配置 Reflections 主要涉及两个核心部分:URL 和扫描器(Scanner)

  • URL:指定类路径的哪些部分需要被扫描。
  • Scanner:负责实际执行扫描任务的对象。

如果没有显式配置扫描器,Reflections 默认使用 TypeAnnotationsScannerSubTypesScanner

3.1. 添加 URL

可以通过构造函数参数或使用 ConfigurationBuilder 来配置 Reflections。

例如,通过包名、类或类加载器实例化 Reflections:

Reflections reflections = new Reflections("com.baeldung.reflections");
Reflections reflections = new Reflections(MyClass.class);
Reflections reflections = new Reflections(MyClass.class.getClassLoader());

由于构造函数支持可变参数,也可以组合使用多种方式:

Reflections reflections = new Reflections("com.baeldung.reflections", MyClass.class);

使用 ConfigurationBuilder 也能达到相同效果:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections"))));

除了 forPackage()ClasspathHelper 还提供了 forClass()forClassLoader() 等方法来添加 URL。

3.2. 添加扫描器(Scanner)

✅ Reflections 提供了多个内置扫描器:

  • FieldAnnotationsScanner – 扫描字段上的注解
  • MethodParameterScanner – 扫描方法/构造函数参数、返回值类型和参数注解
  • MethodParameterNamesScanner – 获取方法/构造函数的参数名称
  • TypeElementsScanner – 扫描字段和方法,以全限定类名为键存储元素
  • MemberUsageScanner – 扫描成员(方法/字段等)的使用情况
  • TypeAnnotationsScanner – 扫描类上的运行时注解
  • SubTypesScanner – 扫描类的父类和接口,支持反向查找子类型
  • MethodAnnotationsScanner – 扫描方法上的注解
  • ResourcesScanner – 收集所有非类资源文件

可以通过构造函数或 ConfigurationBuilder 添加扫描器:

Reflections reflections = new Reflections("com.baeldung.reflections"), 
  new FieldAnnotationsScanner(), 
  new MethodParameterScanner());

或者使用 ConfigurationBuilder

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections"))
  .setScanners(new FieldAnnotationsScanner(), new MethodParameterScanner()));

3.3. 使用 ExecutorService 实现异步扫描

除了 URL 和 Scanner,还可以使用 ExecutorService 实现并发扫描:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections"))
  .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner())
  .setExecutorService(Executors.newFixedThreadPool(4)));

也可以直接调用 .useParallelExecutor() 方法,默认会根据 CPU 核心数创建固定线程池。

3.4. 添加过滤器

过滤器用于控制哪些类应该被包含或排除在扫描之外。比如排除测试包:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections"))
  .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner())
  .filterInputsBy(new FilterBuilder().excludePackage("com.baeldung.reflections.test")));

到这里,我们已经完成了 Reflections 的基本配置,接下来是实际使用阶段。

4. 查询功能详解

调用 Reflections 构造函数后,扫描器会扫描对应的 URL,并将结果存储在 Multimap 中。我们可以使用 Reflections 提供的查询方法来访问这些数据。

4.1. 查找子类型

获取某个类的所有子类:

public Set<Class<? extends Scanner>> getReflectionsSubTypes() {
    Reflections reflections = new Reflections(
      "org.reflections", new SubTypesScanner());
    return reflections.getSubTypesOf(Scanner.class);
}

4.2. 查找带特定注解的类

获取带有某注解的所有类:

public Set<Class<?>> getJDKFunctinalInterfaces() {
    Reflections reflections = new Reflections("java.util.function", 
      new TypeAnnotationsScanner());
    return reflections.getTypesAnnotatedWith(FunctionalInterface.class);
}

4.3. 查找带特定注解的方法

使用 MethodAnnotationsScanner 查找方法:

public Set<Method> getDateDeprecatedMethods() {
    Reflections reflections = new Reflections(
      "java.util.Date", 
      new MethodAnnotationsScanner());
    return reflections.getMethodsAnnotatedWith(Deprecated.class);
}

4.4. 查找带特定注解的构造函数

public Set<Constructor> getDateDeprecatedConstructors() {
    Reflections reflections = new Reflections(
      "java.util.Date", 
      new MethodAnnotationsScanner());
    return reflections.getConstructorsAnnotatedWith(Deprecated.class);
}

4.5. 查找特定参数类型的方法

public Set<Method> getMethodsWithDateParam() {
    Reflections reflections = new Reflections(
      java.text.SimpleDateFormat.class, 
      new MethodParameterScanner());
    return reflections.getMethodsMatchParams(Date.class);
}

4.6. 查找特定返回值类型的方法

public Set<Method> getMethodsWithVoidReturn() {
    Reflections reflections = new Reflections(
      "java.text.SimpleDateFormat", 
      new MethodParameterScanner());
    return reflections.getMethodsReturn(void.class);
}

4.7. 查找资源文件

使用 ResourcesScanner 搜索 classpath 下的资源:

public Set<String> getPomXmlPaths() {
    Reflections reflections = new Reflections(new ResourcesScanner());
    return reflections.getResources(Pattern.compile(".*pom\\.xml"));
}

4.8. 其他常用查询方法

✅ Reflections 还提供以下查询方法(这里列出部分):

  • getMethodsWithAnyParamAnnotated
  • getConstructorsMatchParams
  • getConstructorsWithAnyParamAnnotated
  • getFieldsAnnotatedWith
  • getMethodParamNames
  • getConstructorParamNames
  • getFieldUsage
  • getMethodUsage
  • getConstructorUsage

5. 将 Reflections 集成到构建流程中

我们可以使用 gmavenplus-plugin 将 Reflections 集成到 Maven 构建过程中,并将扫描结果保存为文件:

<plugin>
    <groupId>org.codehaus.gmavenplus</groupId>
    <artifactId>gmavenplus-plugin</artifactId>
    <version>1.5</version>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
                <scripts>
                    <script><![CDATA[
                        new org.reflections.Reflections(
                          "com.baeldung.refelections")
                            .save("${outputDirectory}/META-INF/reflections/reflections.xml")]]>
                    </script>
                </scripts>
            </configuration>
        </execution>
    </executions>
</plugin>

之后,在运行时可以通过 collect() 方法加载已保存的结果,避免重新扫描:

Reflections reflections
  = isProduction() ? Reflections.collect() : new Reflections("com.baeldung.reflections");

⚠️ 注意:isProduction() 是自定义逻辑,用于判断是否处于生产环境。

6. 总结

在这篇文章中,我们介绍了 Reflections 库的核心功能与配置方式,包括 URL 设置、Scanner 配置、过滤器以及如何将其集成进 Maven 构建流程。

总的来说,Reflections 是一个非常实用的工具库,特别适合需要动态扫描类路径并处理元数据的场景。

完整代码示例可在 GitHub 获取。


原始标题:A Guide to the Reflections Library