Как я могу получить список всех реализаций интерфейса программно на Java? - PullRequest
62 голосов
/ 07 декабря 2008

Могу ли я сделать это с помощью отражения или что-то в этом роде?

Ответы [ 11 ]

35 голосов
/ 26 мая 2015

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

  1. отражений библиотека довольно популярна, если вы не возражаете против добавления зависимости. Это будет выглядеть так:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
  2. ServiceLoader (согласно ответу Эриксона), и это будет выглядеть так:

    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().

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

27 голосов
/ 07 декабря 2008

Что сказал Эриксон, но если вы все еще хотите это сделать, взгляните на Размышления Со своей страницы:

Используя Reflections, вы можете запросить ваши метаданные для:

  • получить все подтипы некоторого типа
  • получить аннотации всех типов с некоторой аннотацией
  • получить аннотации для всех типов с некоторой аннотацией, включая соответствие параметров аннотации
  • получить все методы, помеченные некоторыми
24 голосов
/ 07 декабря 2008

В общем, это дорого делать. Чтобы использовать отражение, класс должен быть загружен. Если вы хотите загрузить каждый класс, доступный в пути к классам, это займет время и память, и не рекомендуется.

Если вы хотите избежать этого, вам нужно будет реализовать собственный анализатор файлов классов, который работал бы более эффективно, а не с отражением. В этом подходе может помочь библиотека разработки байт-кода.

Механизм поставщика услуг - это обычное средство для перечисления реализаций подключаемой службы. Используйте ServiceLoader в Java 6 или реализуйте свой собственный в более ранних версиях. Я предоставил пример в другом ответе.

11 голосов
/ 14 августа 2017

У Spring довольно простой способ добиться этого:

public interface ITask {
    void doStuff();
}

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

Затем вы можете автоматически связать список типа ITask, и Spring заполнит его всеми реализациями:

@Service
public class TaskService {

    @Autowired
    private List<ITask> tasks;
}
4 голосов
/ 07 декабря 2008

То, что сказал Эриксон, лучше всего. Вот связанная ветка вопросов и ответов - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

Библиотека Apache BCEL позволяет читать классы, не загружая их. Я считаю, что это будет быстрее, потому что вы должны пропустить шаг проверки. Другая проблема с загрузкой всех классов с помощью загрузчика классов заключается в том, что вы будете испытывать огромное влияние на память, а также непреднамеренно запускать любые статические блоки кода, которые вы, вероятно, не хотите делать.

Ссылка на библиотеку Apache BCEL - http://jakarta.apache.org/bcel/

4 голосов
/ 07 декабря 2008

Да, первый шаг - определить «все» классы, которые вас интересуют. Если у вас уже есть эта информация, вы можете перечислить каждую из них и использовать instanceof для проверки связи. Соответствующая статья здесь: http://www.javaworld.com/javaworld/javatips/jw-javatip113.html

1 голос
/ 07 декабря 2008

Кроме того, если вы пишете плагин IDE (где то, что вы пытаетесь сделать, является относительно распространенным), то IDE обычно предлагает вам более эффективные способы доступа к иерархии классов текущего состояния пользовательского кода.

0 голосов
/ 20 января 2019

С ClassGraph все довольно просто:

Groovy-код для поиска реализаций my.package.MyInterface:

@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.className
0 голосов
/ 14 декабря 2018

Новая версия ответа @ kaybee99, но теперь возвращающая то, что просит пользователь: реализации ...

Spring имеет довольно простой способ добиться этого:

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

}

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

Затем вы можете автоматически связать список типа ITask, и Spring заполнит его всеми реализациями:

@Service
public class TaskService {

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

    if ( tasks != null)
    for (ITask<?> taskImpl: tasks) {
        taskImpl.doStuff();
    }   
}
0 голосов
/ 01 августа 2018

Попробуйте ClassGraph . (Отказ от ответственности, я автор). ClassGraph поддерживает сканирование для классов, которые реализуют данный интерфейс, во время выполнения или во время сборки, но также и многое другое. ClassGraph может построить абстрактное представление всего графа классов (все классы, аннотации, методы, параметры методов и поля) в памяти, для всех классов в пути к классам или для классов в пакетах из белого списка, и вы можете запросить этот класс классов, однако ты хочешь. ClassGraph поддерживает больше механизмов спецификации classpath и загрузчиков классов , чем любой другой сканер, а также без проблем работает с новой системой модулей JPMS, поэтому, если вы основываете свой код на ClassGraph, ваш код будет максимально переносимым. См. API здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...