Статьи

Обнаружение аннотаций Java

Аннотации оказываются широко популярными в наши дни. Многие основанные на аннотациях фреймворки (Spring, JPA, Tapestry и многие другие) видят свет, и даже небольшие проекты используют метапрограммирование на основе аннотаций для лучшего разделения проблем. Аннотации замечательные и обязательно останутся здесь.

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

Во-первых, вы найдете ресурсы, где мы можем найти аннотированные классы. Вы можете посмотреть системное свойство « java.class.path » (также известное как CLASSPATH ) или использовать Classloader или ServletContexts, чтобы получить список ресурсов. Теперь вы просматриваете каждый ресурс и проверяете аннотации, которые вас касаются.

Самый простой способ сканирования ресурса — это загрузить его через Classloader и использовать API-интерфейс Java Reflection для поиска указанной аннотации. Однако этот подход поможет вам только найти аннотации, которые видны во время выполнения, и загрузка каждого ресурса в память будет занимать ненужный объем памяти. В противном случае вы можете использовать библиотеки обработки байтов ASM или Javassist . Эти библиотеки обрабатывают байт-код и могут видеть аннотации времени выполнения. А поскольку они не загружают ваши ресурсы в память, они занимают мало места.

Ну, чтобы помочь вам здесь, мы написали небольшую библиотеку под названием Annovention с использованием Javassist. Идея Annovention заключается в том, чтобы помочь вам быстро найти аннотированные классы, поля или методы. Когда у вас есть соответствующие классы, вы можете запустить логику своего домена или делать все, что вам нужно.

Загрузите Annovention здесь:  http://code.google.com/p/annovention/

Читайте подробный блог здесь:  http://anismiles.wordpress.com/2010/07/26/discovering-java-annotations/

Как это работает?

Annovention работает по схеме подписки и прослушивания.

  1. Вы создаете слушателей аннотации обнаружения.

    1. Annovetion опора класс , полевая и метод обнаружение аннотаций уровня и каждый из этих слушателей должна осуществить ClassAnnotationDiscoveryListener , FieldAnnotationDiscoveryListener или MethodAnnotationDiscoveryListener интерфейсов соответственно.
    2. У каждого слушателя есть метод supportAnnotations (), который возвращает массив имен аннотаций. Например, для прослушивания аннотаций @Entity и @EntityListeners массив будет иметь вид:
      new String[] {Entity.class.getName(), EntityListeners.class.getName()}
    3. У каждого слушателя есть метод found (), который вызывается каждый раз, когда появляется соответствующая аннотация с именем класса, именем поля, именем метода и обнаруженным именем аннотации.
    4. Обратите внимание, что ваши слушатели получают только имена классов, полей и методов. Вы должны использовать Java Reflection для их создания. Помните Class.forName () ?
  2. Теперь есть класс Discoverer, который выполняет все открытия. Для этого нужны ресурсы и слушатели. Annovention поставляется с реализацией ClasspathDiscoverer. При этом используется системное свойство «java.class.path» и создается массив ресурсов для сканирования. Вам нужно зарегистрировать своих слушателей в классе Discoverer, чтобы получать уведомления.

Образец использования

public class SampleAnnotationDiscoverer {

public static void main (String args []) {
// Get a classpath discoverer instance
Discoverer discoverer = new ClasspathDiscoverer();

// Register class annotation listener
discoverer.addAnnotationListener(new MyClassAnnotationListener());
// Register field annotation listener
discoverer.addAnnotationListener(new MyFieldAnnotationListener());
// Register method annotation listener
discoverer.addAnnotationListener(new MyMethodAnnotationListener());

// Fire it
discoverer.discover();
}

/** Dummy ClassAnnotation listener */
static class MyClassAnnotationListener implements ClassAnnotationDiscoveryListener {
private static Log log =
LogFactory.getLog(MyClassAnnotationListener.class);

@Override
public void discovered(String clazz, String annotation) {
log.info("Discovered Class(" + clazz + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @Entity and @EntityListeners annotations.
return new String[] {
Entity.class.getName(),
EntityListeners.class.getName()};
}
}

/** Dummy FieldAnnotation listener */
static class MyFieldAnnotationListener implements FieldAnnotationDiscoveryListener {
private static Log log =
LogFactory.getLog(MyFieldAnnotationListener.class);

@Override
public void discovered(String clazz, String field, String annotation) {
log.info("Discovered Field(" + clazz + "." + field + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @Id and @Column annotations.
return new String[] {
Id.class.getName(),
Column.class.getName()};
}
}

/** Dummy FieldAnnotation listener */
static class MyMethodAnnotationListener implements MethodAnnotationDiscoveryListener {

private static Log log =
LogFactory.getLog(MyMethodAnnotationListener.class);

@Override
public void discovered(String clazz, String method, String annotation) {
log.info("Discovered Method(" + clazz + "." + method + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @PrePersist, @PreRemove and @PostPersist annotations.
return new String[] {
PrePersist.class.getName(),
PostPersist.class.getName(),
PreRemove.class.getName()};
}
}
}

Как продлить?

Вам просто нужно поиграть с классами Discoverer и Filter . Общий поток:

  1. Discoverer вызывает метод findResources (), который возвращает массив URL, а затем
  2. Он перебирает каждый URL и применяет фильтр, и
  3. Затем просканируйте ресурс на наличие аннотаций и
  4. Если правильная аннотация найдена, соответствующие слушатели намекаются.

Вы можете написать свою собственную реализацию Filter или использовать класс FilterImpl, который поставляется с Annovention. Затем просто расширьте класс Discoverer и реализуйте метод findResources () любым удобным для вас способом. Затем просто вызовите метод Discover () . Легко?