Найти классы Java, реализующие интерфейс

Некоторое время назад я наткнулся на кусок кода, который использовал часть стандартных функций Java для поиска классов, которые реализовали данный интерфейс. Я знаю, что функции были скрыты в некотором нелогичном месте, но они могут использоваться для других классов, поскольку подразумевается имя пакета. Тогда мне это не нужно, поэтому я забыл об этом, но теперь я это сделаю, и я не могу найти функции снова. Где можно найти эти функции?

Изменить: я не ищу никаких функций IDE или чего-то еще, а скорее того, что может быть выполнено в приложении Java.

Ответ 1

Еще недавно я собрал пакет для того, чтобы делать то, что вы хотите, и многое другое. (Мне это нужно для утилиты, которую я писал). Он использует библиотеку ASM. Вы можете использовать отражение, но ASM оказалось лучше.

Я помещаю свой пакет в библиотеку с открытым исходным кодом, которую у меня есть на моем веб-сайте. Библиотека находится здесь: http://software.clapper.org/javautil/. Вы хотите начать с класса ClassFinder.

Утилита, которую я написал для нее, является RSS-ридером, который я все еще использую каждый день, поэтому код действительно работает. Я использую ClassFinder для поддержки API-интерфейса подключаемого модуля в читателе RSS; при запуске он выглядит в паре каталогов деревьев для банок и файлов классов, содержащих классы, реализующие определенный интерфейс. Это намного быстрее, чем вы могли ожидать.

Библиотека лицензируется BSD, поэтому вы можете безопасно связать ее с вашим кодом. Источник доступен.

Если это полезно для вас, помогите себе.

Обновление. Если вы используете Scala, вы можете найти эту библиотеку более Scala -дружественно.

Ответ 2

Spring может сделать это для вас...

BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);

TypeFilter tf = new AssignableTypeFilter(CLASS_YOU_WANT.class);
s.addIncludeFilter(tf);
s.scan("package.you.want1", "package.you.want2");       
String[] beans = bdr.getBeanDefinitionNames();

N.B. TypeFilter важен, если вы хотите получить правильные результаты! Здесь вы также можете использовать фильтры исключения.

Сканер можно найти в банке spring -context, в реестре spring - beans, фильтр типа находится в spring -core.

Ответ 3

Мне действительно нравится библиотека отражений для этого.

Он предоставляет множество различных типов сканеров (getTypesAnnotatedWith, getSubTypesOf и т.д.), и он мертв просто писать или расширять свои собственные.

Ответ 4

Код, о котором вы говорите, звучит как ServiceLoader,, который был представлен на Java 6 для поддержки функции, которая была определена с Java 1.3 или ранее. По соображениям производительности это рекомендуемый подход для поиска реализации интерфейса во время выполнения; если вам нужна поддержка этого в старой версии Java, я надеюсь, что вы найдете мою реализацию полезной.

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

Ответ 5

Аннотации уровня пакета

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

Пока довольно сложно найти все классы в JVM, на самом деле довольно легко просматривать иерархию пакетов.

Package[] ps = Package.getPackages();
for (Package p : ps) {
  MyAno a = p.getAnnotation(MyAno.class)
  // Recursively descend
}

Затем просто сделайте свою аннотацию аргументом массива класса. Затем в вашем пакете -info.java для определенного пакета поставьте MyAno.

Я добавлю более подробную информацию (код), если люди заинтересованы, но, скорее всего, получат эту идею.

Сервисный загрузчик MetaInf

Чтобы добавить к ответу @erickson, вы также можете использовать подход загрузчика услуг. У Kohsuke есть потрясающий способ генерации необходимого материала META-INF, необходимого для подхода к загрузчику услуг:

http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html

Ответ 6

Вы также можете использовать Extensible Component Scanner (extcos: http://sf.net/projects/extcos) и искать все классы, реализующие интерфейс следующим образом:

Set<Class<? extends MyInterface>> classes = new HashSet<Class<? extends MyInterface>>();

ComponentScanner scanner = new ComponentScanner();
scanner.getClasses(new ComponentQuery() {
    @Override
    protected void query() {
        select().
        from("my.package1", "my.package2").
        andStore(thoseImplementing(MyInterface.class).into(classes)).
        returning(none());
    }
});

Это работает для классов в файловой системе, в банках и даже для тех, которые находятся в виртуальной файловой системе JBoss. Он также предназначен для работы в автономных приложениях, а также в любом контейнере для веб-приложений или приложений.

Ответ 7

В полной общности эта функциональность невозможна. Механизм Java ClassLoader гарантирует только возможность запросить класс с определенным именем (включая pacakge), а ClassLoader может предоставить класс или может утверждать, что он не знает этого класса.

Классы могут (и часто) загружаться с удаленных серверов, и они могут даже быть построены на лету; совсем нетрудно написать ClassLoader, который возвращает действительный класс, который реализует данный интерфейс для любого имени, которое вы просите у него; Список классов, реализующих этот интерфейс, будет тогда бесконечным по длине.

На практике наиболее распространенным случаем является URLClassLoader, который ищет классы в списке каталогов файловой системы и JAR файлах. Итак, вам нужно получить URLClassLoader, затем перебрать эти каталоги и архивы, а для каждого найденного в них файла класса запросить соответствующий объект Class и просмотреть его метод getInterfaces().

Ответ 8

Очевидно, Class.isAssignableFrom() указывает вам, реализует ли отдельный класс данный интерфейс. Таким образом, проблема заключается в получении списка классов для тестирования.

Насколько я знаю, нет прямого пути от Java, чтобы спросить у загрузчика классов "список классов, которые вы могли бы загрузить". Таким образом, вам нужно будет сделать это самостоятельно, итерации через видимые банки, вызвав Class.forName(), чтобы загрузить класс, а затем проверить его.

Однако, это немного проще, если вы просто хотите узнать классы, реализующие данный интерфейс, из тех, которые действительно были загружены:

  • с помощью Java Instrumentation, вы можете вызвать Instrumentation.getAllLoadedClasses()
  • через отражение, вы можете запросить поле ClassLoader.classes данного ClassLoader.

Если вы используете инструментальную технику, то (как поясняется в ссылке) происходит то, что ваш класс "агент" называется по существу, когда JVM запускается и передает объект Instrumentation. В этот момент вы, вероятно, захотите "сохранить его позже" в статическом поле, а затем введите свой основной код приложения, чтобы получить список загруженных классов.

Ответ 9

Если вы задали вопрос с точки зрения работы с запущенной программой, вам нужно посмотреть на пакет java.lang. *. Если вы получаете объект Class, вы можете использовать метод isAssignableFrom, чтобы проверить, является ли он интерфейсом другого класса.

Существует не простой встроенный способ поиска, такие инструменты, как Eclipse, строят индекс этой информации.

Если у вас нет определенного класса объектов класса для проверки, вы можете посмотреть объект ClassLoader, используйте метод getPackages() и создайте свой собственный итератор иерархии пакетов.

Просто предупреждение, хотя эти методы и классы могут быть довольно медленными.