Как я могу получить список всех реализаций интерфейса программно в Java?
Могу ли я сделать это с отражением или что-то в этом роде?
Ответы
Ответ 1
Я искал некоторое время, и, кажется, есть разные подходы, вот резюме:
-
Библиотека отражений довольно популярна, если вы не возражаете против добавления зависимости. Это будет выглядеть так:
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
-
ServiceLoader (согласно ответу erickson), и это будет выглядеть так:
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
-
аннотация на уровне пакета. вот пример:
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, которые всегда будут ограничены классами, которые уже были загружены, если только вы не выполните поиск по каталогу.
Ответ 2
Что сказал Эриксон, но если вы все еще хотите это сделать, взгляните на " Размышления". Со своей страницы:
Используя Reflections, вы можете запросить ваши метаданные для:
- получить все подтипы некоторого типа
- получить аннотации всех типов с некоторой аннотацией
- получить аннотации для всех типов, включая сопоставление параметров аннотации
- получить аннотации всех методов с некоторыми
Ответ 3
В общем, это дорого делать. Чтобы использовать отражение, класс должен быть загружен. Если вы хотите загрузить каждый класс, доступный на пути к классам, это займет время и память, и не рекомендуется.
Если вы хотите избежать этого, вам нужно будет реализовать собственный анализатор файлов классов, который работал бы более эффективно, чем рефлексия. В этом подходе может помочь библиотека разработки байт-кода.
Механизм Service Provider - это обычное средство для перечисления реализаций подключаемой службы. Используйте ServiceLoader
в Java 6 или реализуйте свой собственный в более ранних версиях. Я привел пример в другом ответе.
Ответ 4
У 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;
}
Ответ 5
Да, первым шагом является определение "всех" классов, о которых вы заботитесь. Если у вас уже есть эта информация, вы можете перечислить каждую из них и использовать instanceof для проверки отношения. Связанная статья находится здесь: http://www.javaworld.com/javaworld/javatips/jw-javatip113.html
Ответ 6
Что эриксон сказал лучше всего. Вот связанный вопрос и ответ нить - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html
Библиотека Apache BCEL позволяет вам читать классы без их загрузки. Я считаю, что это будет быстрее, потому что вы сможете пропустить этап проверки. Другая проблема с загрузкой всех классов с использованием загрузчика классов заключается в том, что вы будете испытывать огромное влияние на память, а также непреднамеренно запускать любые статические блоки кода, которые вы, вероятно, не хотите делать.
Ссылка на библиотеку Apache BCEL - http://jakarta.apache.org/bcel/
Ответ 7
С ClassGraph все довольно просто:
Groovy-код для поиска реализаций my.package.MyInterface
:
@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
}
Ответ 8
Кроме того, если вы пишете плагин IDE (где то, что вы пытаетесь сделать, является относительно распространенным), тогда среда IDE обычно предлагает более эффективные способы доступа к иерархии классов текущего состояния кода пользователя.
Ответ 9
Я столкнулся с той же проблемой. Мое решение состояло в том, чтобы использовать рефлексию для изучения всех методов класса ObjectFactory, исключая те методы, которые не были createXXX(), возвращающие экземпляр одного из моих связанных POJO. Каждый обнаруженный класс добавляется в массив Class [], который затем передается в экземплярный вызов JAXBContext. Это хорошо работает, нужно только загрузить класс ObjectFactory, который все равно понадобился. Мне нужно только поддерживать класс ObjectFactory, задачу, выполняемую вручную (в моем случае, потому что я начал с POJO и использовал схему), или может быть сгенерирован по необходимости xjc. В любом случае, он эффективен, прост и эффективен.
Ответ 10
Новая версия @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();
}
}
Ответ 11
Самый надежный механизм для перечисления всех классов, которые реализуют данный интерфейс, в настоящее время ClassGraph, потому что он обрабатывает максимально широкий массив механизмов спецификации пути к классам, включая новую систему модулей JPMS. (Я автор.)
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
foundImplementingClass(ci); // Do something with the ClassInfo object
}
}