Что такое аннотация java
Перейти к содержимому

Что такое аннотация java

  • автор:

Аннотации в Java. Не путать с комментариями

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

Вы узнаете аннотацию по символу @ в начале имени: @Override – стандартная аннотация Javа, которая предупреждает, что ниже мы что-то переопределим:

class SomeClass < void method() < System.out.println("Работает метод родительского класса."); > > class AnotherClass extends SomeClass < // наследуем методы SomeClass в новом классе @Override void method() < // переопределяем метод System.out.println("Работает метод класса-потомка."); > > 

Если в имени метода из AnotherClass будет опечатка, компилятор учтет @Override и выдаст ошибку. Без аннотации он не заметил бы подвоха и безропотно создал бы новый метод в дополнение к method из SomeClass.

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

Зачем нужны аннотации Java

  • автоматически создавать конфигурационные XML-файлы и дополнительный Java-код на основе исходного аннотированного кода;
  • документировать приложения и базы данных параллельно с их разработкой;
  • проектировать классы без применения маркерных интерфейсов;
  • быстрее подключать зависимости к программным компонентам;
  • выявлять ошибки, незаметные компилятору;
  • решать другие задачи по усмотрению программиста.

Поясним понятие «маркерный интерфейс». Интерфейсы без каких-либо методов действуют как маркеры. Они лишь говорят компилятору, что объекты классов, которые имплементируют такой интерфейс без методов, должны иметь отличительные черты, восприниматься иначе. Например, java.io.Serializable, java.lang.Cloneable, java.util.EventListener. Маркерные интерфейсы ещё известны как «теги» — они добавляют общий тег ко всем унаследованным классам и объединяют их в одну категорию.

При первом появлении в Java EE 5 аннотации были представлены как инструмент, который ускоряет разработку больших web-сервисов и клиентских приложений. Как это работает?

Обработка аннотации в Джава

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

Обработчиками обычно выступают библиотеки и утилиты, которые можно брать у сторонних авторов (или создавать самостоятельно) и прикреплять к проекту в среде разработки. Способ подключения зависит от IDE или системы сборки. В Maven обработчики подключают с помощью модуля annotation-user или плагина maven-compiler-plugin.

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

Пишем первую аннотацию на Java

Допустим, у нас есть веб-сервис, который поддерживает несколько версий одного функционала для соблюдения совместимости. И есть обработчик аннотаций, который позволяет компилятору выбирать нужные версии. На минутку забудем о существовании Git 🙂

Где хранить данные о версии и авторе функционала? Конечно же, в аннотации. Напишем её. Новую аннотацию объявляют с помощью ключевого слова @interface:

public class SomeClass < public @interface version < private float v(); // номер версии private String author() default “Аноним"; // автор > // остальное содержимое класса > 

Это немного искусственный, но зато простой и наглядный пример аннотации на Java. Мы добавили два атрибута, которые выглядят как методы. Отличие в том, что при объявлении атрибутов никогда не используют оператор throws и не назначают параметров. Значениями могут выступать:

  • примитивные типы Java,
  • классы или снабженные параметрами обращения к классам,
  • перечисления,
  • другие аннотации,
  • массивы из вышеперечисленных элементов.

Можно указывать значения по умолчанию, что мы и сделали выше с полем author. При постановке аннотации атрибуты с дефолтными значениями можно пропускать.

@version(v=1.0f); // автор остаётсяАнонимом”, а "f" после числа ставим для явного указания на тип float SomeClass < // . > 

Чтобы нашу аннотацию использовали только по назначению, вернёмся к её объявлению и укажем, где и когда она должна работать:

@Target(ElementType.TYPE) /* Аннотация применима только к классам, а не к пакетам, отдельным методам, переменным и т.д. */ @Retention(RetentionPolicy.RUNTIME) /* Применяется во время выполнения программы. Если бы нам нужно было применять аннотацию к исходному коду на этапе компиляции программы, мы бы указали RetentionPolicy.SOURCE.*/ public @interface version < // . > 

Остается ассоциировать аннотацию с нужным классом и запустить программу.

Используем аннотации Java для сравнения баз данных

Напишем что-нибудь более сложное и полезное. Нам нужно свести несколько БД в одну. Для начала сравним их, чтобы найти одинаковые поля и значения, устранить дубли и внести новую информацию из каждого источника. Могут возникнуть и сопутствующие задачи: одни значения потребуется отформатировать, другие — проигнорировать, всё вместе вывести в виде .xls-отчёта.

Мы наметили конфигурацию работы с данными, остаётся её реализовать. Вот здесь на сцену выходят аннотации. С их помощью мы можем задать (а если потребуется — и скорректировать) способы обработки каждого поля. При этом конфигурация будет выглядеть абсолютно прозрачно: читатель кода сразу поймёт, что к чему применено. И ход исполнения программы не будет затронут. Волшебно! Пишем:

// Сначала укажем, что аннотацию надо применять на уровне ПОЛЯ @Target(ElementType.FIELD) // Использовать аннотацию надо во время выполнения программы @Retention(RetentionPolicy.RUNTIME) public @interface FieldInspector < //создаём аннотацию, придумываем ей имя // Выбираем, сравнивать ли источники. По умолчанию — да. boolean compareSources() default true; /* Получаем формат значения в отчёте, а если не получаем — подставляем формат по умолчанию — native. */ СhooseTheFormat displayFormat() default СhooseTheFormat.NATIVE; // Получаем ID текущего поля для сопоставления в нескольких базах int id(); // Уточняем, какое имя использовать для поля в таблице. String field_name() default ""; // Какие источники сравнивать — формируем список. TheSource[] sourcesToCompare() default <>; > 

Теперь ассоциируем аннотацию с полем. Допустим, у нас в БД книги:

@FieldInspector(id = 2, field_name = "TITLE") private String book_title; // переменная хранит название книги

Присвоили id, присвоили имя полю в отчёте. Сверка значений со всеми базами запустится по умолчанию, т.к мы не указали для compareSources значение false. А если мы заведомо знаем, что искать нужно не во всех БД, а в конкретных? Например, сюжет есть не у любой книги. Задаём источники вручную:

// проверять только пьесы и беллетристику @FieldInspector(id = 3, field_name = "PLOT" sourcesToCompare =TheSource.PLAYS, TheSource.FICTION>) private String plot; 

Если у нас есть поле «Примечания», значение которого не нужно сверять, мы можем перед переменной для хранения примечаний поставить такую аннотацию:

@FieldInspector(id = 300, field_name = "NOTES", compareSources = false) private String notes; 

Неплохо, но как сделать, чтобы правила обработки выбирались в зависимости от значения поля? Можно связать правила с классом обработчика, чтобы тот выбирал, как поступать с каждым полем. Для этого потребуется ещё одна короткая аннотация:

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface RuleApplier < // массив строк, где хранятся параметры обработчика, по умолчанию — параметров нет String[] parameters() default <>; // класс, который отвечает за применение правил к каждому источнику Class?> processor() default MyRuleApplier.class; > 

Проверим, как это работает. Давайте не сравнивать данные о литературе, которая поступила в библиотеку до 1990 года — допустим, эти данные у нас были только в одной базе:

@FieldInspector(id = 4, label = "THE YEAR OBTAINED", displayFormat = СhooseTheFormat.YEAR_FORMAT>) @RuleApplier(processor = IgnoreWhatWasObtainedBefore.class, parameters = < "1990" >) private int book_obtained;

Теперь реагировать на значения менее 1990 в поле «год поступления» мы доверяем конкретному пользовательскому классу. Это даёт нам больше гибкости в работе с данными, но помните, что аннотации — вещь статическая. Какое значение получил обработчик на входе, с тем и разбирается. Интерактива с динамической сменой значений пользователем не получится.

Аннотация методов, annotation

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

Аннотация выполняет следующие функции :

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

Встроенные аннотации : @Override, @Deprecated, @SuppressWarnings

Встроенные аннотации отслеживаются средой разработки IDE и применяются к java-коду метода :

@Override Проверка переопределения метода. IDE вызывает предупреждение компиляции, если метод не найден в родительском классе.
@Deprecated IDE отмечает, что метод устарел и вызывает предупреждение компиляции, если метод используется.
@SuppressWarnings Аннотация указывает IDE подавить предупреждения компиляции.

Аннотации, применяемые к другим аннотациям : @Retention, @Documented, @Target, @Inherited

@Retention Определяет, как отмеченная аннотация будет храниться — в коде, в скомпилированном классе или во время работы кода.
@Documented Отмечает аннотацию для включения в документацию.
@Target Отмечает аннотацию как ограничивающую, какие элементы java-аннотации могут быть к ней применены.
@Inherited Отмечает, что аннотация может быть расширенна подклассами аннотируемого класса.

Первоначально в платформе Java имелся механизм, предваряющий механизм аннотаций — например, модификатор transient или тэг @deprecated. В сентябре 2002 года сообществу Java представлен документ JSR-175, описывающий основные тезисы по аннотациям. Он был утвержден в 2004 году. Аннотации стали доступны в самом языке начиная с версии Java 5.0 и описаны в JSR-269. В версии Java 6 аннотации были интегрированы в компилятор javac.

Пример аннотации :
public class Animal < public void speak() < >> public class Cat extends Animal < @Override // Аннотация говорит о том, что этот метод переопределен public void speak() < System.out.println("Meow."); >>

Синтаксис аннотации, @interface

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

Для описания новой аннотации используется ключевое слово @interface. Например :

public @interface Description

Пример использования аннотации Description :

@Description(title="title", version=2, text="text") public class Sample < // . >

Пример аннотации с параметрами:

import java.lang.annotation.*; @Target(value=ElementType.FIELD) @Retention(value= RetentionPolicy.RUNTIME) public @interface Name

В данном примере аннотация включает в себя несколько полей (name, type), которые можно задать как обязательными, так и необязательными. В последнем случае подставляется default значение поля.

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

Параметры аннотации

@Retention

Аннотация @Retention позволяет определить жизненный цикл аннотации : будет она присутствовать только в исходном коде, в скомпилированном файле, или она будет также видна и в процессе выполнения. Выбор нужного типа аннотации @Retention зависит от того, как будет использоваться данная аннотацию. Например, генерировать что-то побочное из исходных кодов, или в процессе выполнения «стучаться» к классу через reflection.

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

Параметр @Target указывает, что именно должно быть помечено аннотацией. Это может быть поле, метод, тип и т.д. Для этого следует использовать параметры к аннотации.

@Target(ElementType.PACKAGE) только для пакетов
@Target(ElementType.TYPE) только для классов
@Target(ElementType.CONSTRUCTOR) только для конструкторов
@Target(ElementType.METHOD) только для методов
@Target(ElementType.FIELD) только для атрибутов(переменных) класса
@Target(ElementType.PARAMATER) только для параметров метода
@Target(ElementType.LOCAL_VARIABLE) только для локальных переменных

В случае, если необходимо, что бы аннотация использовалась больше чем для одного типа параметров, то можно указать @Target следующим образом:

@Target(< ElementType.PARAMETER, ElementType.LOCAL_VARIABLE >)

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

Параметр @Documented указывает, что помеченные таким образом аннотацией класс/метод/поле должны быть добавлены в javadoc. Например, класс, помеченный аннотацией без @Documented, будет выглядеть так:

public class TestClass extends java.lang.Object

А если в описание аннотации добавить @Documented, получим:

@ControlledObject(name="name") public class TestClass extends java.lang.Object

Использование аннотации

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

public class User < public static enum Permission < USER_MANAGEMENT, CONTENT_MANAGEMENT >private List permissions; public List getPermissions() < return new ArrayList(permissions); > // . >

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

@Retention(RetentionPolicy.RUNTIME) public @interface PermissionRequired

Предположим, что у нас есть некоторое действие, право на выполнение которого мы хотим ограничить, например, 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()))< // выполнить действие >>

Пример анализатора аннотации

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

Что должен делать анализатор? Он использует reflection для доступа к аннотируемым данным. Пример анализатора для класса @Test:

public class AnnotationAnalyzer < public void parse(Classclazz) throws Exception < Method[] methods = clazz.getMethods(); int pass = 0; int fail = 0; for (Method method : methods) < if (method.isAnnotationPresent(Test.class)) < try < // вызов аннотируемого метода method.invoke(null); pass++; >catch (Exception e) < fail++; >> > > >

Сочетание использования аннотации и reflection позволяет выполнить определенную проверку и вызвать метод на исполнение через invoke. Анализатор готов к использованию. Для использования атрибутов аннотации расширим код.

public class AnnotationAnalyzer < public void analyze(Classclazz) throws Exception < Method[] methods = clazz.getMethods(); int pass = 0; int fail = 0; for (Method method : methods) < if (method.isAnnotationPresent(Test.class)) < // Получаем доступ к атрибутам Test test = method.getAnnotation(Test.class); Class expected = test.expected(); try < method.invoke(null); pass++; >catch (Exception e) < if (Exception.class != expected) < fail++; >else < pass++; >> > > > >

После получения доступа к атрибуту аннотации определяем ее значение. В нашем случае это значение типа Class, так как expected — это ожидаемая ошибка и мы будем получать exception.

Пример использования класса анализа аннотации:

public class Demo < public static void main(String [] args) < AnnotationAnalyzer analyzer = new AnnotationAnalyzer(); analyzer.analyze(MyTest.class); >>

Аннотации — Java: Корпоративные приложения на Spring Boot

В Java аннотации встречаются часто, но особенно много их в Spring Boot. Чтобы понять, как работает фреймворк, нужно разобраться в устройстве аннотаций. В этом уроке мы познакомимся с ними и узнаем, как они работают.

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

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

package io.hexlet.spring; import org.springframework.boot.SpringApplication; // Определения аннотаций — это обычный код, который нужно импортировать import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @RestController // Аннотация на уровне класса @RequestMapping("/api") // Аннотация на уровне класса @SpringBootApplication public class Application  @Autowired // Аннотация на уровне поля private UserRepository userRepository; public static void main(String[] args)  SpringApplication.run(Application.class, args); > @GetMapping("/hello") // Аннотация на уровне метода public String sayHello(@RequestParam(name = "name", required = false, defaultValue = "Guest") String name)  // Аннотация на уровне параметров return "Hello, " + name + "!"; > @PostMapping("/greet") // Аннотация на уровне метода public String greet(@RequestBody String name)  // Аннотация на уровне параметров return "Greetings, " + name + "!"; > > 

Некоторые аннотации выглядят как метка — @RestController , другие похожи на вызов метода с параметрами — @RequestMapping(«/api») . Принцип работы от этого не меняется: аннотация не превращается в вызов метода, она остается меткой с дополнительными данными.

Зачем аннотации вообще нужны? Во-первых, они значительно сокращают объем шаблонного кода — это повторяющийся одинаковый код, который нужен для конфигурации приложения, соединения его частей друг с другом или других задач. Раньше ту же задачу решали с помощью конфигурационных XML-файлов, которые иногда были просто огромными. Из-за этого Java-программистов часто называли XML-программистами. Активное использование аннотаций существенно упростило этот процесс.

Встроенные аннотации

Подавляющее большинство аннотаций в реальных проектах написаны разработчиками библиотек, а еще буквально несколько аннотаций встроено прямо в Java. Например, аннотация @Deprecated позволяет отметить класс или метод как устаревший. Эту информацию затем можно увидеть в подсказках редактора. Такая аннотация помогает другим программистам при выборе классов и методов для реализации их задач:

@Deprecated public void oldMethod()  // Дальше продолжается какой-то код > 

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

// Пример из Spring Boot public class User implements UserDetails  @Override public boolean isAccountNonExpired()  return true; > @Override public boolean isAccountNonLocked()  return true; > @Override public boolean isCredentialsNonExpired()  return true; > > 

Кастомные аннотации

С такими аннотациями мы будем встречаться чаще всего. Изучить их работу заранее невозможно — каждая конкретная аннотация обрабатывается своим образом и приводит к своим последствиям. Причем в большинстве случаев программист до конца не знает, что на самом деле происходит внутри.

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

Изучим пример типичного контроллера на Spring Boot. Здесь можно насчитать около десятка аннотаций, причем из разных пакетов:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; @RestController @AllArgsConstructor @RequestMapping("/api") public class PostsController  @Autowired private final PostRepository repository; @PostMapping("/posts") @ResponseStatus(HttpStatus.CREATED) PostDTO create(@Valid @RequestBody PostDTO postData) throws JsonProcessingException  // Тут логика > @GetMapping("/posts/") @ResponseStatus(HttpStatus.OK) PostDTO show(@PathVariable Long id)  // Тут логика > > 

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

Устройство кастомных аннотаций

Посмотрим, как определять и обрабатывать аннотации. Эти знания помогут разобраться в принципе работы аннотаций, а еще вопросы на эту тему часто задают на собеседованиях. Напишем аннотацию @LogExecutionTime , которая замеряет время выполнения помеченного ей метода:

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecutionTime  > 

Иронично, что определение аннотации само помечено ими. В коде выше мы видим три обязательные аннотации:

  • @interface определяет саму аннотацию
  • @Retention определяет жизненный цикл аннотации, то есть указывает, как долго аннотация должна оставаться с кодом. В этом случае аннотация должна быть доступна в рантайме, потому что именно так мы будем ее обрабатывать
  • @Target определяет, где мы будем применять аннотацию (например, в методах)

Аннотация готова, можно начинать применять ее. При этом в работе кода ничего не поменяется, потому что обработчик еще не написан:

// Мы должны импортировать нашу аннотацию import  путь>.LogExecutionTime; public class SomeService  @LogExecutionTime public void serve() throws InterruptedException  Thread.sleep(1500); // Выполняем какую-то задачу > public void anotherMethod()  // Этот метод еще не отмечен аннотацией, // поэтому время выполнения метода не измеряется и не логируется > > 

Дальше мы напишем обработчик аннотации. Это обычный Java-код, поэтому нужно убедиться, что он выполняется до того, как исполнение дойдет до кода с аннотациями. В нашем случае обработчик выполняется в методе main() :

import java.lang.reflect.Method; public class Main  public static void main(String[] args)  var service = new SomeService(); // Итерируем все методы класса for (Method method : SomeService.class.getDeclaredMethods())  // Проверяем, есть ли у метода аннотация @LogExecutionTime if (method.isAnnotationPresent(LogExecutionTime.class))  var startTime = System.currentTimeMillis(); try  // Выполняем метод с аннотацией LogExecutionTime method.invoke(service); > catch (Exception e)  e.printStackTrace(); > long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; System.out.println("Executed method: " + method.getName()); System.out.println("Execution time: " + executionTime + " milliseconds"); > > > > 

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

Параметры кастомных аннотаций

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

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecutionTime  boolean enabled(); long threshold() default 0; // Пороговое время в миллисекундах > 

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

public class SomeService  @LogExecutionTime(enabled = true) public void serve() throws InterruptedException  Thread.sleep(1500); // Выполняем какую-то задачу > @LogExecutionTime(enabled = true, threshold = 100) public void anotherMethod()  

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Аннотации в JAVA

Аннотации — это форма метаданных. Они предоставляют информацию о программе, при том сами частью программы не являются.

Применение
  • Информация для компилятора. Могут использоваться компилятором для обнаружения ошибок и подавления предупреждений.
  • Обработка во время компиляции и развертывания. Программа может создавать код, XML-файлы и т.п. на основе аннотаций.
  • Обработка во время выполнения. Некоторые аннотации могут использоваться во время выполнения программы.
Синтаксис

Начинаются с @ , могут включать элементы, которым присваиваются значения:

@Author( name = "Benjamin Franklin" date = "3/27/2003" ) class MyClass

Если такой элемент один, его имя можно опустить:

@SupressWarnings("unchecked") void MyMethod()

Если таких элементов нет, можно опустить скобки. Можно использовать несколько аннотаций в одном объявлении:

@Author(name = "Jane Doe") @EBook class MyClass

Аннотации могут быть повторяющимися.

@Author(name = "Jane Doe") @Author(name = "John Smith") class MyClass
Где в коде можно использовать аннотации

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

Аннотации, использующиеся с типами, называются аннотациями типов. Примеры таких аннотаций:

  • Создание экземпляра класса:
new @Interned MyObject(); 
  • Приведение к типу:
myString = (@NonNull String) str;
  • Имплементация:

class UnmodifiableList implements @Readonly List

  • Объявление бросаемых исключений:
void monitorTemperature() throws @Critical TemperatureException

Создание аннотации

Синтаксис

Описание аннотации напоминает описание интерфейса. Оно начинается с @interface , а его элементы похожи на методы, которые могут иметь дефолтные значения.

Пример

Допустим, в какой-то IT-компании тела всех классов начинаются с комментариев, содержащих важную информацию:

public class Generation3List extends Generation2List < // Author: John Doe // Date: 3/17/2002 // Current revision: 6 // Last modified: 4/12/2004 // By: Jane Doe // Reviewers: Alice, Bill, Cindy // class code goes here >

Описание аннотации, которая заменит комментарии:

@interface ClassPreamble < String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; // Можно использовать массив String[] reviewers(); >

Использование созданной аннотации:

@ClassPreamble ( author = "John Doe", date = "3/17/2002", currentRevision = 6, lastModified = "4/12/2004", lastModifiedBy = "Jane Doe", reviewers = ) public class Generation3List extends Generation2List

Замечание: для добавления аннотации в Javadocs нужно использовать @Documented:

import java.lang.annotation.*; @Documented @interface ClassPreamble < // Описание элементов аннотации >

Предопределенные аннотации

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

Аннотации, использующиеся компилятором

Располагаются в пакете java.lang.

Помеченный этой аннотацией элемент устарел и больше не должен использоваться (это стоит отметить в Javadoc). При наличии такого элемента в программе компилятор сгенерирует предупреждение.

// Комментарий Javadoc: /** * @deprecated * объяснение, почему метод устарел. */ @Deprecated static void deprecatedMethod()

Информирует компилятор о том, что аннотируемый элемент должен переопределять элемент родительского класса. При некорректном переопределении компилятор сгенерирует ошибку.

@Override int overriddenMethod()

Подавляет генерируемые компилятором предупреждения.

Предупреждения делятся на непроверенные (unchecked) и устаревшие (deprecation). Первые возникают при использовании устаревшего кода, написанного до дженериков, вторые — при использовании кода, помеченного аннотацией @Deprecated.

Можно подавить как одну категорию, так и обе сразу:

@SuppressWarnings()

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

@SafeVarargs // На самом деле не безопасно! static void m(List. stringLists) < Object[] array = stringLists; ListtmpList = Arrays.asList(42); array[0] = tmpList; //Написано неверно, но скомпилируется без предупреждения String s = stringLists[0].get(0); //ClassCastException >

Используется при описании функционального интерфейса. Подчеркивает, что это именно функциональный интерфейс.

@FunctionalInterface public interface Predicate

Аннотации, применимые к другим аннотациям (мета-аннотации)

Располагаются в пакете java.lang.annotation.

Указывает, сколько хранится отмеченная аннотация.

  • RetentionPolicy.SOURCE . Отмеченная аннотация сохраняется только на уровне исходного кода и игнорируется компилятором.
  • RetentionPolicy.CLASS . Сохраняется компилятором во время компиляции, но игнорируется JVM.
  • RetentionPolicy.RUNTIME . Сохраняется JVM для использования во время выполнения программы.

Указывает, что аннотация, должна быть задокументирована в Javadoc (по умолчанию аннотации не документируются).

Определяет права доступа аннотации (к каким элементам ее можно применять). В аннотации @Target указывается одно из следующих значений:

  • ElementType.ANNOTATION_TYPE . Применяется к аннотации
  • ElementType.CONSTRUCTOR . Применяется к конструктору.
  • ElementType.FIELD . Применяется к полю или свойству.
  • ElementType.LOCAL_VARIABLE . Применяется к локальной переменной.
  • ElementType.METHOD . Применяется к методу.
  • ElementType.PARAMETER . Применяется к параметру метода.
  • ElementType.TYPE . Применяется к любому элементу класса.

Аннотация будет наследоваться дочерним классом (по умолчанию аннотации не наследуются). Применима только к описаниям классов.

Указывает, что аннотация повторяющаяся.

Повторяющиеся аннотации

Определение

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

Пример

Допустим, вам надо написать аннотацию, запускающую метод в заданное время или по определенному расписанию. В примере созданная аннотация @Schedule будет запускать метод каждый последний день месяца и каждую пятницу в 23:00.

@Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup()
Создание повторяющейся аннотации

Для обеспечения обратной совместимости повторяющиеся аннотации хранятся в контейнере аннотаций, который автоматически генерируется java компилятором. Для генерации нужны следующие описания:

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

import java.lang.annotation.Repeatable; @Repeatable(Schedules.class) public @interface Schedule
  1. Описание контейнера аннотаций

Контейнер должен содержать массив повторяющихся аннотаций.

public @interface Schedules
Получение повторяющихся аннотаций

Reflection API предоставляет методы для получения аннотаций. При получении повторяющихся аннотаций поведение методов, которые возвращают одну аннотацию (например, AnnotatedElement.getAnnotation(Class) ) не меняется. Если нужно вернуть более одной, то необходимо сначала получить контейнер. Таким образом устаревший код продолжает работать. Также, для получения повторяющихся аннотаций можно использовать методы Java SE 8 (‘AnnotatedElement.getAnnotationsByType(Class)’).

Для получения информации о всех методах, обратитесь к документации класса AnnotatedElement.

Аннотации типов

Определение

Аннотации типов — аннотации, которые применяются вместе с типами. Везде, где вы видите тип, можно использовать эту аннотацию. Например, с оператором new, при приведении, при имплементации и при использовании throws.

Создание аннотации типов

Для создания аннотации типов в @Target указываются следующие значения, либо одно из них:

@Target () public @interface Test

TYPE_PARAMETER , означает, что аннотацию можно применять к переменным типа (например, MyClass ). TYPE_USE , разрешает применение с любыми типами.

Применение

Аннотации типов предназначены для улучшенного анализа программ и более строгой проверки типов. Например, @NonNull String str; . Java SE8 определяет аннотации типов, но не реализует. Вместо этого предлагается использовать сторонние фреймворки, реализующие их (Checker Framework).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *