Сканирование аннотаций Java во время выполнения

Каков наилучший способ поиска всего класса для аннотированного класса?

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

Знаете ли вы библиотеку или средство Java для этого?

Изменить: Я думаю о чем-то вроде новой функциональности для веб-служб Java EE 5 или EJB. Вы комментируете свой класс с помощью @WebService или @EJB, и система обнаруживает эти классы во время загрузки, чтобы они были доступны удаленно.

Ответ 1

Используйте org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

API

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

ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(<DO_YOU_WANT_TO_USE_DEFALT_FILTER>);

scanner.addIncludeFilter(new AnnotationTypeFilter(<TYPE_YOUR_ANNOTATION_HERE>.class));

for (BeanDefinition bd : scanner.findCandidateComponents(<TYPE_YOUR_BASE_PACKAGE_HERE>))
    System.out.println(bd.getBeanClassName());

Ответ 2

И еще одно решение Google размышления.

Быстрый просмотр:

  • Spring решение - это путь, если вы используете Spring. В противном случае это большая зависимость.
  • Использование ASM напрямую немного громоздко.
  • Использование Java Assist напрямую также неудобно.
  • Annovention - суперлегкий и удобный. Пока нет интеграции maven.
  • Google размышляет в коллекциях Google. Индексирует все, а затем очень быстро.

Ответ 3

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

Ответ 4

Если вы хотите действительно легкий вес (без зависимостей, простого API, файла jar размером 15 kb) и очень быстрого решения, посмотрите annotation-detector, найденный в https://github.com/rmuller/infomas-asl

Отказ от ответственности: я автор.

Ответ 5

Вы можете использовать API Java Pluggable Annotation Processing API для записи обработчика аннотаций, который будет выполняться во время процесса компиляции и будет собирать все аннотированные классы и создавать индексный файл для использования во время выполнения.

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

Вышеупомянутый механизм уже реализован в библиотеке ClassIndex.

Чтобы использовать его, аннотируйте свою аннотацию с помощью @IndexAnnotated мета-аннотации. Это создаст во время компиляции индексный файл: META-INF/annotations/com/test/YourCustomAnnotation, содержащий список всех аннотированных классов. Вы можете получить индекс во время выполнения, выполнив:

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)

Ответ 7

Попробуйте Scannotation.

Его можно использовать для поиска в classpath или в каталоге вашего веб-приложения lib для конкретных аннотаций.

Ответ 9

С помощью Spring вы также можете просто написать следующее, используя класс AnnotationUtils. то есть:

Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);

Для получения дополнительной информации и различных методов ознакомьтесь с официальными документами: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html.

Ответ 10

Немного offtopic, но Spring также делает что-то подобное, используя < context: component-scan > , который вы могли бы изучить исходный код?

Spring предоставляет возможность автоматического обнаружения "стереотипных" классов [...]. Чтобы автоопределить эти классы и зарегистрировать соответствующий beans, необходимо включить элемент [context: component-scan].

Ответ 11

Слишком поздно отвечать. Я бы сказал, что лучше использовать библиотеки, такие как ClassPathScanningCandidateComponentProvider или как Scannotations

Но даже после того, как кто-то захочет попробовать его с помощью classLoader, я написал несколько своих статей, чтобы напечатать аннотации из классов в пакете:

public class ElementScanner {

public void scanElements(){
    try {
    //Get the package name from configuration file
    String packageName = readConfig();

    //Load the classLoader which loads this class.
    ClassLoader classLoader = getClass().getClassLoader();

    //Change the package structure to directory structure
    String packagePath  = packageName.replace('.', '/');
    URL urls = classLoader.getResource(packagePath);

    //Get all the class files in the specified URL Path.
    File folder = new File(urls.getPath());
    File[] classes = folder.listFiles();

    int size = classes.length;
    List<Class<?>> classList = new ArrayList<Class<?>>();

    for(int i=0;i<size;i++){
        int index = classes[i].getName().indexOf(".");
        String className = classes[i].getName().substring(0, index);
        String classNamePath = packageName+"."+className;
        Class<?> repoClass;
        repoClass = Class.forName(classNamePath);
        Annotation[] annotations = repoClass.getAnnotations();
        for(int j =0;j<annotations.length;j++){
            System.out.println("Annotation in class "+repoClass.getName()+ " is "+annotations[j].annotationType().getName());
        }
        classList.add(repoClass);
    }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

/**
 * Unmarshall the configuration file
 * @return
 */
public String readConfig(){
    try{
        URL url = getClass().getClassLoader().getResource("WEB-INF/config.xml");
        JAXBContext jContext = JAXBContext.newInstance(RepositoryConfig.class);
         Unmarshaller um =  jContext.createUnmarshaller();
         RepositoryConfig rc = (RepositoryConfig) um.unmarshal(new File(url.getFile()));
         return rc.getRepository().getPackageName();
        }catch(Exception e){
            e.printStackTrace();
        }
    return null;

}
}

И в файле конфигурации вы помещаете имя пакета и отменяете его класс.

Ответ 12

API-интерфейс Classloader не имеет метода "перечисления", поскольку загрузка класса является активностью "по требованию" - обычно у вас есть тысячи классов в вашем пути к классам, только часть из которых когда-либо понадобится ( Только rt.jar составляет 48 МБ!).

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

Простым подходом является список соответствующих классов в установочном файле (xml или что-то, что вам нравится); если вы хотите сделать это автоматически, ограничьте себя одним JAR или одним классом.

Ответ 13

Я не уверен, поможет ли это вам или нет, но вы можете посмотреть проект apache commons-discovery.

проект обнаружения

Ответ 14

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

Обычно эта проблема была решена путем включения файла свойств или файла .xml с именами классов в нем.

Мне было бы интересно услышать и лучший ответ.

Ответ 15

Google Reflections кажется намного быстрее, чем Spring. Нашел этот запрос функции, который ссылается на эту разницу: http://www.opensaga.org/jira/browse/OS-738

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

Ответ 16

Если вы ищете альтернативу отражениям, я бы рекомендовал Panda Utilities - AnnotationsScanner. Он не содержит Guava (у Guava есть ~ 3 МБ, Panda Utilities имеет ~ 200kb) сканер, основанный на исходном коде библиотеки отражений.

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

Простой пример использования AnnotationsScanner:

AnnotationsScanner scanner = AnnotationsScanner.createScanner()
        .includeSources(ExampleApplication.class)
        .build();

AnnotationsScannerProcess process = scanner.createWorker()
        .addDefaultProjectFilters("net.dzikoysk")
        .fetch();

Set<Class<?>> classes = process.createSelector()
        .selectTypesAnnotatedWith(AnnotationTest.class);

Ответ 17

Google Reflection, если вы хотите открыть интерфейсы.

Spring ClassPathScanningCandidateComponentProvider не обнаруживает интерфейсы.

Ответ 19

В Spring есть класс AnnotatedTypeScanner.
Этот класс внутренне использует

ClassPathScanningCandidateComponentProvider

Этот класс имеет код для фактического сканирования ресурсов classpath. Это делается с помощью метаданных класса, доступных во время выполнения.

Можно просто расширить этот класс или использовать тот же класс для сканирования. Ниже приведено определение конструктора.

/**
     * Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
     * 
     * @param considerInterfaces whether to consider interfaces as well.
     * @param annotationTypes the annotations to scan for.
     */
    public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {

        this.annotationTypess = Arrays.asList(annotationTypes);
        this.considerInterfaces = considerInterfaces;
    }