31
May

Аннотации в Java (java annotation types)

Posted in: Java technologies, J2SE |

Продолжаю серию статей о нововведениях в Java (начиная с версии 1.5). На этот раз разговор пойдет об аннотациях (annotation type).

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

Вот основные варианты использования аннтоаций:

  • предоставлять необходимую информацию для компилятора;
  • предоставлять метаданные различным инструментам для генерации кода, конфигураций и т.д.;
  • использоваться в коде во время выполнения програмного кода (reflection).

Аннотации могут быть применены, например, к декларациям классов, полей, методов, ну и конечно же аннотаций :).
Для описания новой аннотации используется ключевое слово @interface. Вот банальный пример аннотации:

public @interface Description {
    String title();
    int version() default 1;
    String text();
}

И пример ее использования:

@Description(title="title", version=2, text="text")
public class Clazz { /* */ }

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

Если у аннотации нет элементов, ее называют маркером (marker annotation type). В этом случае при использовании аннотации круглые скобки можно не писать.

В случае, когда аннтоация указывается для другой аннотации, первую называют мета-аннотацией (meta-annotation type).
Достаточно часто вам придется сталкиваться с мета-аннтоацией Retention. Она показывает, как долго необходимо хранить аннтоацию и инициализируется одним из трех значений:

  • RetentionPolicy.SOURCE - аннотация используется на этапе компиляции и должна отбрасываться компилятором;
  • RetentionPolicy.CLASS - аннтоация будет записана в class-файл компилятором, но не должна быть доступна во время выполнения (runtime);
  • RetentionPolicy.RUNTIME - аннотация будет записана в class-файл и доступна во время выполнения через reflection.

Тут есть еще одна вещь, на которую хочу обратить ваше внимание: по умолчанию у всех аннотаций стоит RetentionPolicy.CLASS. Это мне кажется недодумкой. В исходниках JDK очень часто используется эта policy, но вот в разработке нужна именно RetentionPolicy.RUNTIME. К сожалению, ничего уже не поменяется из-за обратной совместимости.

Пришло время привести реальный пример использования аннотаций при программировании на Java.

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

public class User {
    public static enum Permission {
        USER_MANAGEMENT, CONTENT_MANAGEMENT
    }

    private List<Permission> permissions;

    public List<Permission> getPermissions() {
        return new ArrayList<Permission>(permissions);
    }

    // ...
}

Создадим аннтоацию, которую затем будем использовать для проверки прав:

@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionRequired {
    User.Permission value();
}

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

@PermissionRequired(User.Permission.USER_MANAGEMENT)
public class UserDeleteAction {
    public void invoke(User user) { /* */ }
}

Теперь используя reflection можно принимать решение, разрешать или не разрешать выполнение определенного действия:

User user = ...;
Class<?> actionClass = ...;
PermissionRequired permissionRequired =
        actionClass.getAnnotation(PermissionRequired.class);
if (permissionRequired != null)
    if (user != null && user.getPermissions().contains(permissionRequired.value()))
        // выполнить действие

Вот и все.

Буду благодарен за любые замечания и комментарии по поводу статьи.
Всего вам хорошего.

18 Comments »

RSS feed for comments on this post.



June 1, 2008 #

C0nst,

еще возможный (и частый вариант) типа параметров аннотации - java.lang.Class

Про саму статью - пример как есть понятный только веб-програмерам.

А в общем прикольно, только такой вопрос - кто целевая группа этого поста ?

June 1, 2008 #

Давно не писали, заждался …

фраза:


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

Ну не совсем так, например можно в качестве значения аннотации использовать Class
@interface BU{
Class foo ();
}

@BU (foo = String.class)

public class Barra {

Хотя скажу прямо, sun-овцы явно не додумали когда писали спеку-аннотаций, например, встречаются такие гадкие конструкции (в гибернате):

@OneToMany (mappedBy = “department”, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
protected Set employees = new HashSet();

Так и подмывает сказать и нафига нужно задавать статическую информацию об структуре кода в виде строки текста (мало мне опечаток). Мможно было бы добавить такой вид синтаксиса и возложить контроль за проверкой корректности на компилятор

@OneToMany (mappedBy = pack.db.Employee#department, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
protected Set employees = new HashSet();

June 1, 2008 #

А-а-а-а, меня опередили с подловленной ошибкой и одной и той же.

ну да ладно, можно было бы рассказать об страшных временах когда был XDoclet, commons annotations для сравнения хотя бы.

P.S. Может у меня что не так, но пока я написал этот коммент страница раза три самопроизвольно перезагрузилась (opera 9.5). Может какой-то гадкий js-скрипт?

June 2, 2008 #

Хорошая статья, но есть несколько недочетов. Про один уже сказали. Еще в качестве типов у элементов аннотации могут выступать другие аннотации. Например, твой вариант @Description можно расширить

public @interface Version {

int major() default 1;
int minor() default 0;

}
public @interface Description {

String title();

Version version() default @Version(major = 1);

String text() default "";

}

Ну и использование

@Description(title = "title", version = @Version(major = 2, minor = 1))
public class Clazz {}

Ну и еще, аннотациями можно также помечать параметры методов.

public void test(@Description(title = "some title") String arg) {

}

LuLok
June 25, 2008 #

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

Tzaf
June 26, 2008 #

Аналогично

Из этой статьи мало что понятно …

Но автору все равно спасибо, старался как никак :)

Trofei
July 2, 2008 #

Извиняюсь, что не по теме, хотел спросить у автора сайта по-поводу вот этой программки http://www.javenue.info/post/49#comment-24798 Пишу здесь, т.к. та тема была старой и не знаю сообщается ли автору (C0nst) о новых комментах.

Moderated: в соответствующей теме ответил на ваш вопрос

Sts
July 12, 2008 #

замечание в принципе не очень важное
но точности ради приведу выдержку из JLS с сайта компании sun
“Note that the at sign (@) and the keyword interface are two distinct tokens”
это собсно значит, что @interface не является новым ключевым словом, а представляет собой два отдельных слова, а значит вы написание “@ interface” так же правильно как и “@interface”

August 5, 2008 #

Блин я два раза перечитал и не понял ни фига((

August 6, 2008 #

а поновее что нибудь будет?

August 7, 2008 #

весь моск себе вынес, нифига разобрать не могу…

August 10, 2008 #

да впринципе ничего сложного, сразу разобрался

August 11, 2008 #

Ага, а что делать со скриптами? мне в них только разбираться сколько придётся!

August 21, 2008 #

Как-нить черкану скриптик с помощью этого) Спс)

August 22, 2008 #

Благодарю за статью . Очень полезная информация.

August 24, 2008 #

Как все же не просто разбораться с Java.

August 27, 2008 #

спасибо! очень помогло в моей проблеме

August 27, 2008 #

Спасибо автору, подчерпнул немного для себя полезного

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>