Сканирование аннотаций Java во время выполнения - PullRequest
229 голосов
/ 03 ноября 2008

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

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

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

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

Ответы [ 17 ]

196 голосов
/ 12 сентября 2009

Использовать 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());
140 голосов
/ 03 августа 2010

И еще одно решение - Google отражений .

Быстрый обзор:

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

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

22 голосов
/ 21 ноября 2011

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

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

17 голосов
/ 27 декабря 2011

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

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

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

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

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
6 голосов
/ 04 ноября 2008

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

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

5 голосов
/ 26 июля 2010

Возможно, вы захотите использовать http://code.google.com/p/annovention/

4 голосов
/ 13 октября 2015

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

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

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

3 голосов
/ 03 ноября 2008

Немного оффтоп, но Spring также делает нечто подобное, используя , который вы, возможно, могли бы изучить в исходном коде?

Spring предоставляет возможность автоматического определения «стереотипных» классов [...]. Для автоматического определения этих классов и регистрации соответствующих bean-компонентов необходимо включить [context: component-scan element].

3 голосов
/ 22 ноября 2017

Слишком поздно, чтобы ответить. Я бы сказал, что лучше использовать библиотеки типа 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;

}
}

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

...