Подтвердить что ты не робот

Поиск всех классов, реализующих определенный интерфейс

Я занимаюсь разработкой приложения (Quartz scheduler), где у нас есть класс работы, который отвечает за фактическое выполнение работы, и нам нужно указать/передать имя класса задания при создании триггера в Quartz планировщик.

Я хочу предоставить точку расширения всем, кто хочет использовать API (помимо некоторых общих заданий, которые я буду предоставлять как часть API). Идея состоит в том, чтобы создать (маркерный) интерфейс, и если кто-то хочет объявить свой класс как класс задания планировщика, все, что им нужно сделать, это (объявить) реализовать интерфейс.

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

Мое требование - не загружать классы во время выполнения, а показывать список пользователей классов, которые реализуют требуемый интерфейс, чтобы пользователь мог выбрать класс, а имя класса можно передать планировщику. Это планировщик Quartz, который в конце будет отвечать за создание экземпляра класса.

Может ли кто-нибудь предложить, как я могу достичь вышеуказанной цели, или есть ли другой лучший способ достичь того, что я пытаюсь сделать?

Edit

Я просмотрел документ ServiceLoader, и кажется, что для реализации службы необходимо создать файл в папке META-INF с именем класс реализации, который заставляет меня думать, что если пользователь моего API хочет 20 различных реализаций, он должен поместить 20 записей в файл, который для меня кажется большим количеством дополнительной работы для конечного пользователя, поскольку каждый класс задания будет создан для выполнения определенного задания, и может быть 100 классов рабочих заданий.

Пожалуйста, исправьте меня, если мое предположение неверно.

4b9b3361

Ответ 1

У меня была аналогичная потребность, когда я хотел убедиться, что любые классы, созданные с использованием определенного интерфейса, всегда были сериализованы. Я создал JavaClassFinder, который просматривает все каталоги в пути к классам и находит все классы, назначаемые интерфейсу, о котором я беспокоюсь. Вот фрагмент кода:

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) {
    foundClasses = new ArrayList<Class<?>>();
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>();
    this.toFind = toFind;
    walkClassPath();
    for (Class<?> clazz : foundClasses) {
        returnedClasses.add((Class<? extends T>) clazz);
    }
    return returnedClasses;
}

Я рад поделиться с вами кодом, если это поможет. Единственное отступление - это то, что это будет обрабатывать только файлы .class - я не добавлял эту функцию для распаковки .jars и чтения файлов классов оттуда. (Но это не было бы большим проектом, чтобы добавить это.)

UPDATE: Я проверил исходный код для вышеуказанного и нашел, что это зависит от множества вспомогательных классов в нашей стандартной библиотеке. Чтобы было проще, я закрепил весь необходимый код, который вы можете скачать из JavaClassFinder.zip. Это будет настроено непосредственно в Eclipse, и вы можете взять любые части кода, который вам нужен.

В проекте вы найдете тест JUnit3, называемый JavaClassFinderTest.java, который показывает вам функции и использование класса JavaClassFinder. Единственной внешней зависимостью, необходимой для запуска теста Junit, является Junit.

Основное использование этой утилиты:

    JavaClassFinder classFinder = new JavaClassFinder();
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class);

Это даст вам список, который содержит любые классы в пути к классам, которые можно назначить из "MyTagInterface.class" (например). Надеюсь, это поможет.

Ответ 2

Вы можете найти ответ здесь.

Я могу предложить использовать org.reflections

введите ссылку здесь

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

Ответ 3

Вероятно, лучший (стандартный) способ сделать это с помощью механизма Java SPI, см. Javadoc. Неудобным (что также является приятной особенностью) является то, что он ожидает, что Jars определит расширения для их перечисления в META-INF/services/your.fully.qualified.Interface.

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

Ответ 4

Я думаю, что org.reflections - это правильное решение, упомянутое Алексом Стибаевым, вам не нужно ссылаться на классы в файле свойств.

Другой подход (который я бы взял) Spring, так как я использую Spring в любом случае для своих приложений и, следовательно, нужны дополнительные зависимости.

Вы можете найти подсказки для решения вашей проблемы с помощью Spring (и альтернативы в других комментариях или ответах) здесь:

В комментариях к вашему вопросу heikkim и polypiel также ссылаются на вопросы с ответами на их решение с помощью Spring:

Ответ 6

Чтобы сделать это в чистой java, самый правильный ответ уже проголосовали (от sam goldberg apr 10,2012)

Однако полный код его излишне сложный. Я использовал его идею и нажал ClassFinder: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.java

Примечание. Это будет работать в автономном приложении (или в любом приложении, использующем обычный путь к классам с банками и dirs), а не в ServerContainer.

Если ваше приложение работает на веб-сервере, вам необходимо знать местоположение вашей войны/уха и искать там. Или вы должны спросить своего родителя classlaoder, где он получает ваши (и другие) источники.

Второе примечание. Я фильтрую классы конечных местоположений, чтобы соответствовать только netx и icedtea-web, поскольку я не искал найденных зависимостей. Поэтому, если вам нужно включить rt.jar, пожалуйста, rmeove thsoe фильтры. Или удалите bootclasspath вообще, если вы, наоборот, не нуждаетесь в нем вообще.