Сканирование аннотаций Java во время выполнения
Каков наилучший способ поиска аннотированного класса по всему пути к классам?
Я делаю библиотеку и хочу, чтобы пользователи могли аннотировать свои классы, поэтому при запуске веб-приложения мне нужно просканировать весь путь к классу для определенной аннотации.
Знаете ли вы библиотеку или средство Java для этого?
Изменить: я думаю о чем-то вроде новой функциональности для веб-служб Java EE 5 или EJB. Вы комментируете свой класс с @WebService
или же @EJB
и система находит эти классы во время загрузки, чтобы они были доступны удаленно.
17 ответов
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());
И еще одно решение - Google размышления.
Быстрый обзор:
- Решение Spring - лучший способ, если вы используете Spring. В противном случае это большая зависимость.
- Использование ASM напрямую немного обременительно.
- Прямое использование Java Assist также неуклюже.
- Annovention супер легкий и удобный. Нет интеграции с Maven.
- Google отражений тянет в коллекции Google. Индексирует все, а затем супер быстро.
Вы можете найти классы с любой данной аннотацией с помощью ClassGraph, а также искать другие критерии, представляющие интерес, например, классы, которые реализуют данный интерфейс. (Отказ от ответственности, я являюсь автором ClassGraph.) ClassGraph может построить абстрактное представление всего графа классов (все классы, аннотации, методы, параметры методов и поля) в памяти для всех классов на пути к классам или для классов в пакеты в белом списке, и вы можете запросить этот граф классов, как вы хотите. ClassGraph поддерживает больше механизмов спецификации classpath и загрузчиков классов, чем любой другой сканер, а также без проблем работает с новой модульной системой JPMS, поэтому, если вы основываете свой код на ClassGraph, ваш код будет максимально переносимым. Смотрите API здесь.
Если вы хотите действительно легкий вес (без зависимостей, простой API, файл jar 15 КБ) и очень быстрое решение, взгляните на annotation-detector
находится по адресу https://github.com/rmuller/infomas-asl
Отказ от ответственности: я автор.
Вы можете использовать Java Pluggable Annotation Processing API для написания процессора аннотаций, который будет выполняться во время процесса компиляции и будет собирать все аннотированные классы и создавать индексный файл для использования во время выполнения.
Это самый быстрый способ сделать аннотированное обнаружение классов, потому что вам не нужно сканировать путь к классам во время выполнения, что обычно является очень медленной операцией. Также этот подход работает с любым загрузчиком классов, а не только с URLClassLoaders, обычно поддерживаемыми сканерами времени выполнения.
Вышеуказанный механизм уже реализован в библиотеке ClassIndex.
Чтобы использовать его, добавьте свои собственные аннотации с помощью мета-аннотации @IndexAnnotated. Во время компиляции будет создан индексный файл: META-INF/annotations/com/test/YourCustomAnnotation, в котором перечислены все аннотированные классы. Вы можете получить доступ к индексу во время выполнения, выполнив:
ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
Есть замечательный комментарий zapp, который тонет во всех этих ответах:
new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)
Используйте ServiceLoader или внедрите свой собственный, если вы не в Java 6.
Возможно, процессор аннотаций может производить необходимые файлы в META-INF/services во время компиляции.
Весна имеет то, что называется 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;
}
Попробуйте Scannotation.
Его можно использовать для поиска пути к классам или в каталоге lib вашего веб-приложения для поиска конкретных аннотаций.
Это слишком поздно, чтобы ответить. Я бы сказал, что лучше пойти по таким библиотекам, как 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;
}
}
А в файле конфигурации вы помещаете имя пакета и распаковываете его в классе.
Возможно, вы захотите использовать http://code.google.com/p/annovention/
С помощью Spring вы также можете просто написать следующее, используя класс AnnotationUtils. то есть:
Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);
Для получения дополнительной информации и различных методов ознакомьтесь с официальными документами: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html
Немного оффтоп, но Spring также делает нечто подобное, используя
Spring предоставляет возможность автоматического определения "стереотипных" классов [...]. Для автоматического определения этих классов и регистрации соответствующих bean-компонентов требуется включение [context: component-scan element].
В Classloader API нет метода "перечисления", потому что загрузка классов - это действие "по требованию" - у вас обычно есть тысячи классов в вашем пути к классам, только часть из которых когда-либо понадобится (rt.jar только 48MB в наше время!).
Таким образом, даже если бы вы могли перечислить все классы, это заняло бы много времени и памяти.
Простой подход состоит в том, чтобы перечислить соответствующие классы в установочном файле (xml или любой другой, который вам подходит); если вы хотите сделать это автоматически, ограничьте себя одним JAR или одним каталогом классов.
Google Reflection, если вы хотите открыть интерфейсы.
весна ClassPathScanningCandidateComponentProvider
не обнаруживает интерфейсы.
Я не уверен, поможет ли это вам или нет, но вы можете заглянуть в проект Apache Commons-Discovery.
Если вы ищете альтернативу отражениям, я бы рекомендовал Panda Utilities - AnnotationsScanner. Это сканер без гуавы (Guava имеет ~3 МБ, Panda Utilities имеет ~200 КБ) сканер, основанный на исходном коде библиотеки отражений.
Он также предназначен для будущих поисков. Если вы хотите сканировать несколько раз включенные источники или даже предоставить API, который позволяет кому-либо сканировать текущий путь к классам, 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);
У Java нет "Открытия". Единственный способ, которым я знаю, - это сканировать каталог, в котором должны быть файлы.class, анализировать имена и использовать их. Ужасно уродливо, может быть, сейчас есть лучшая посылка - я не смотрел несколько лет.
Обычно эту проблему решали путем добавления файла свойств или XML-файла с именами классов.
Мне было бы интересно услышать лучший ответ.
Google Reflections кажется намного быстрее, чем Spring. Нашел этот запрос функции, который устраняет эту разницу: http://www.opensaga.org/jira/browse/OS-738
Это причина использовать Reflections, так как время запуска моего приложения действительно важно во время разработки. Отражения, кажется, также очень просты в использовании для моего случая использования (найти все реализации интерфейса).
Если вам нужна библиотека Scala, используйте Sclasner: https://github.com/ngocdaothanh/sclasner