«1. Введение

Библиотека Reflections работает как сканер путей к классам. Он индексирует отсканированные метаданные и позволяет нам запрашивать их во время выполнения. Он также может сохранять эту информацию, поэтому мы можем собирать и использовать ее в любой момент нашего проекта без необходимости повторного сканирования пути к классам.

В этом руководстве мы покажем, как настроить библиотеку Reflections и использовать ее в наших проектах Java.

2. Зависимость Maven

Чтобы использовать Reflections, нам нужно включить ее зависимость в наш проект:

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

Мы можем найти последнюю версию библиотеки на Maven Central.

3. Настройка отражений

Далее нам нужно настроить библиотеку. Основными элементами конфигурации являются URL-адреса и сканеры.

URL-адреса сообщают библиотеке, какие части пути к классам сканировать, тогда как сканеры — это объекты, которые сканируют заданные URL-адреса.

Если сканер не настроен, библиотека использует TypeAnnotationsScanner и SubTypesScanner по умолчанию.

3.1. Добавление URL-адресов

Мы можем настроить Reflections либо путем предоставления элементов конфигурации в качестве параметров конструктора varargs, либо с помощью объекта ConfigurationBuilder.

Например, мы можем добавить URL-адреса, создав экземпляр Reflections, используя строку, представляющую имя пакета, класс или загрузчик класса:

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

Более того, поскольку Reflections имеет конструктор varargs, мы можем комбинировать все вышеперечисленное типы конфигураций для его создания:

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

Здесь мы добавляем URL-адреса, указывая пакет и класс для сканирования.

Мы можем добиться тех же результатов, используя ConfigurationBuilder:

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

Вместе с методом forPackage() ClasspathHelper предоставляет другие методы, такие как forClass() и forClassLoader(), для добавления URL-адресов в конфигурацию.

3.2. Добавление сканеров

Библиотека Reflections поставляется со многими встроенными сканерами:

    FieldAnnotationsScanner — ищет аннотации полей. MethodParameterScanner — сканирует методы/конструкторы, затем индексирует параметры и возвращает аннотации типов и параметров. MethodParameterNamesScanner — проверяет методы/конструкторы, затем индексирует имена параметров. TypeElementsScanner — проверяет поля и методы, затем сохраняет полное имя в качестве ключа, а элементы — в качестве значений. аннотации времени выполнения SubTypesScanner — ищет суперклассы и интерфейсы класса, позволяя осуществлять обратный поиск подтипов MethodAnnotationsScanner — сканирует аннотации методов ResourcesScanner — собирает все ресурсы, не относящиеся к классу, в коллекцию

Мы можем добавить сканеры в конфигурацию в качестве параметров конструктора Reflections.

Например, давайте добавим первые два сканера из приведенного выше списка:

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-адресов и сканеров, Reflections дает нам возможность асинхронно сканировать путь к классам с помощью ExecutorService.

Мы можем добавить его как параметр конструктора Reflections или через ConfigurationBuilder:

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

Другой вариант — просто вызвать метод useParallelExecutor(). Этот метод настраивает ExecutorService FixedThreadPool по умолчанию с размером, равным количеству доступных основных процессоров.

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. Подтипы

Начнем с получения всех сканеров, предоставляемых Reflections:

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

4.2. Аннотированные типы

Далее мы можем получить все классы и интерфейсы, которые реализуют данную аннотацию.

Итак, давайте получим все функциональные интерфейсы пакета java.util.function:

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. Параметры методов

Кроме того, мы можем использовать MethodParameterScanner для поиска всех методов с данным типом параметра:

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

4.6. Тип возвращаемого значения методов

Кроме того, мы можем использовать один и тот же сканер для получения всех методов с заданным типом возвращаемого значения.

Давайте представим, что мы хотим найти все методы SimpleDateFormat, возвращающие void:

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

4.7. Ресурсы

Наконец, давайте используем ResourcesScanner для поиска заданного имени файла в нашем пути к классам:

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

4.8. Дополнительные методы запросов

Выше было всего лишь несколько примеров, показывающих, как использовать методы запросов Reflections. Тем не менее, существуют и другие методы запросов, которые мы здесь не рассмотрели: с помощью плагина gmavenplus.

    Давайте настроим его для сохранения результатов сканирования в файл:

Позже, вызвав метод collect(), мы можем получить сохраненные результаты и сделать их доступными для дальнейшего использования, без необходимости выполнить новое сканирование:

6. Заключение

В этой статье мы исследовали библиотеку Reflections. Мы рассмотрели различные элементы конфигурации и их использование. И, наконец, мы увидели, как интегрировать 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>

Как всегда, полный код доступен на GitHub.

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

«

In this article, we explored the Reflections library. We covered different configuration elements and their usages. And, finally, we saw how to integrate Reflections into a Maven project’s build lifecycle.

As always, the complete code is available over on GitHub.