Варианты использования для реализации аннотаций - PullRequest
36 голосов
/ 27 июля 2010

Каковы действительные варианты использования для реализации аннотаций?

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

Альтернатива заключается в зеркалировании данных, содержащихся в аннотациях, в DTO, что выглядитнакладные расходы.

Вот пример:

public enum IDType {
    LOCAL,
    URI,
    RESOURCE;
}

@Documented
@Target( { METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Id {
    /**
     * @return
     */
    IDType value() default IDType.LOCAL;
}

с реализацией

public class IdImpl implements Id{

    private final IDType idType;

    public IdImpl(IDType idType){
        this.idType = idType;
    }

    @Override
    public IDType value() {
        return idType;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }

}

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

Предупреждение для приведенного выше примера:

. Тип аннотации Id не должен использоваться в качестве суперинтерфейса для IdImpl

Отредактировано:

Я только что нашел этот пример от Guice :

bind(CreditCardProcessor.class)
    .annotatedWith(Names.named("Checkout"))
    .to(CheckoutCreditCardProcessor.class);

См. Этот Javadoc от Имен .

Есть кто-нибудьнекоторая информация, почему существует это ограничение или имеются в виду другие варианты использования?

Ответы [ 4 ]

20 голосов
/ 28 июля 2010

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

Давайте создадим искусственный пример. Скажем, у нас есть генератор документации. Он читает аннотацию @Docu из заданных классов и печатает атрибут description. Как это:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

Печать:

This is a static class!  
This is another static class!

Теперь мы хотим, чтобы класс мог не только статически аннотироваться, но и добавлять информацию о времени выполнения в документацию. Мы очень рады использовать аннотацию @Docu большую часть времени, но есть особые случаи, когда нам нужна специальная документация. Возможно, мы захотим добавить документацию по производительности для некоторых методов. Мы можем сделать это, позволив классу реализовать аннотацию. Генератор сначала проверяет аннотацию и, если ее нет, проверяет, реализует ли класс аннотацию. Если это так, он добавляет класс в список аннотаций.

Вот так (всего две дополнительные строки кода в генераторе):

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class,
                DynamicClass.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
            else if (Arrays.asList(c.getInterfaces()).contains(Docu.class))
                result.add((Docu) c.newInstance());
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

class DynamicClass implements Docu {

    public DynamicClass() {
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
            // ignore exception to make debugging a little harder
        }
    }

    @Override
    public String description() {
        long millis = System.currentTimeMillis();
        new DynamicClass();
        millis = System.currentTimeMillis() - millis;
        return "This is a dynamic class. I run on "
                + System.getProperty("os.name")
                + ". The construction of an instance of this class run for "
                + millis + " milliseconds.";
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Docu.class;
    }

}

Вывод:

This is a static class!  
This is another static class!  
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds.

Вы не должны так сильно менять генератор кода, потому что вы можете использовать класс в качестве замены аннотации.

Другим примером может быть платформа, которая использует аннотации или XML в качестве конфигурации. У вас может быть один процессор, который работает с аннотациями. Если вы используете XML в качестве конфигурации, вы можете генерировать экземпляры классов, которые реализуют аннотации, и ваш процессор работает с ними без единого изменения! (конечно, есть другие способы достижения того же эффекта, но это ОДИН способ сделать это)

4 голосов
/ 09 марта 2012

JAXBIntroductions является хорошим примером: он позволяет настраивать аннотации JAXB с использованием файлов XML. На ум приходят два основных варианта использования: настройка классов, к которым у вас нет доступа к исходному тексту, или разные конфигурации для одного класса.

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

0 голосов
/ 28 декабря 2015

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

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

@Id
class BeanA {}

// No annotation
class BeanB {}

Реализация по умолчанию;

private static final Id DEFAULT_ID = new Id() {

    @Override
    public IDType value() {
        return IDType.LOCAL;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }
};

Обработка;

Id beanId = (bean.getClass().isAnnotationPresent(Id.class))
    ? bean.getClass().getAnnotation(Id.class)
    : DEFAULT_ID;
0 голосов
/ 28 июля 2010

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

Внушайте бедную душу, которая придет за вами, чтобы поддерживать и отлаживать этот код, или другой, которому нужно написать инструмент для создания кода, и предполагает, что типы аннотаций являются прямыми или кто-то другой, кто просто использовал такую ​​аннотацию, даже не думая о том, что может произойти и что делать об этом. К тому времени, когда он обнаружит этот взлом и найдет способ устранить его, он умрет от грыжи - или эквивалентной болезни :-) Ожидается, что аннотации будут чисто декларативными утверждениями, интерпретируемыми исключительно инструментом codegen, который работает отдельно от аннотированного кода и обрабатывает это как данные.

Взгляните по-новому на этот код и попытайтесь честно сказать, что такое рациональный смысл для чего-то вроде:

 public Class<? extends Annotation> annotationType() { 
     return Id.class; 
 } 

и это все еще мелочь по сравнению с тем, что люди могут вставить в код.

Аннотации не место для практики хакерства - это то, что компилятор пытается передать. Вы точно знаете, когда и как может выполняться код в «реализации» аннотации? Включая CTOR? Что доступно, а что нет на данный момент? Что безопасно позвонить? Компилятор этого не делает - для проверки реальной безопасности такого хака компилятору потребуется довольно тяжелый статический анализ. Так что вместо этого он просто выдает предупреждение, чтобы, когда что-то пошло не так, люди не могли обвинить компиляцию, ВМ и все остальное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...