- Аннотация методов, annotation
- Встроенные аннотации : @Override, @Deprecated, @SuppressWarnings
- Аннотации, применяемые к другим аннотациям : @Retention, @Documented, @Target, @Inherited
- Пример аннотации :
- Синтаксис аннотации, @interface
- Параметры аннотации
- @Retention
- @Target
- Использование аннотации
- Пример анализатора аннотации
- Spring и JDK 8: Вы все еще используете @Param и name/value в Spring MVC аннотациях? Тогда статья для Вас
- Annotation @param in Java
- Related Article — Java Annotation
Аннотация методов, 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 следующим образом:
В данном случае аннотацию можно использовать только для параметров метода и для локальных переменных.
Параметр @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.
Пример использования класса анализа аннотации:
Spring и JDK 8: Вы все еще используете @Param и name/value в Spring MVC аннотациях? Тогда статья для Вас
Разрабатывая учебный проект по Spring Boot 2 решил поэкспериментировать с @Param в запросах Spring Data JPA, а точнее c их отсутствием:
@Transactional(readOnly = true) public interface UserRepository extends JpaRepository < @Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") OptionalfindByEmailIgnoreCase(@Param("email") String email); List findByLastNameContainingIgnoreCase(@Param("lastname") String lastName); >
(про магию, как работает второй метод есть в старой публикации По следам Spring Pet Clinic).
Убрав @Param можно убедится, что Spring прекрасно работает и без них. Я слышал про параметр в компиляции, который позволяет не дублировать названия в аннотациях, но я ничего не специального не делал, поэтому решил покопать поглубже подебажить.
Если Вы еще пользуетесь аннотациями из заголовка статьи, Spring Boot и JDK 8, прошу под кат:
UPDATE: аннотации @PathVariable и @RequestParam все еще часто нужны, чтобы приложение работало корректно. Но их атрибуты value/name уже не обязательны: соответствие ищется по именам переменных.
- Первое, что я попробовал- поменять имя в параметре ( mail вместо email ):
@Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") Optional findByEmailIgnoreCase(String mail);
Caused by: java.lang.IllegalStateException: Using named parameters for method public abstract java.util.Optional ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase(java.lang.String) but parameter 'Optional[mail]' not found in annotated query 'SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)'! at org.springframework.data.jpa.repository.query.JpaQueryMethod.assertParameterNamesInAnnotatedQuery(JpaQueryMethod.java:125) ~[spring-data-jpa-2.1.3.RELEASE.jar:2.1.3.RELEASE]
Видно, что используются 2 стратегии: StandardReflectionParameterNameDiscoverer и LocalVariableTableParameterNameDiscoverer . Первая использует JDK8 JEP 118: Access to Parameter Names at Runtime. Согласно SPR-9643, если не получается определить имена параметров по первой стратегии, Spring пробует использовать «ASM-based debug symbol analysis».
- В интернете много информации по Java 8 Parameter Names, необходима компиляция с флагом -parameters . Иду в настройки Spring Boot проекта IDEA:
Да, действительно включена… А что, если я соберу и запущу проект через Maven?
Результат тот же!
- Включаю в настройках Maven вывод debug, компилирую проект и вижу:
[DEBUG] Goal: org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) . true
Похоже, что maven-compiler-plugin уже настроен в spring-boot-starter-parent , откуда по умолчанию наследуются проекты spring-boot при генерации через SPRING INITIALIZR. Переходим туда и (только для Spring Boot 2) точно, плагин там настроен:
Unable to detect parameter names for query method ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase! Use @Param or compile with -parameters on JDK 8.
- наши рассуждения верны
- на основе второй стратегии ASM информацию достать не удалось (хотя я запускался через Debug)
ИТОГ: флаг -parameters в Spring Boot 2 включен по умолчанию, поэтому, если вы наследуетесь от spring-boot-starter-parent , то имена параметров определяются в рантайме и @Param , @RequestParam , @PathVariable больше не требуются. Меньше кода, меньше ошибок.
Для Spring Boot 1.x флаг компиляции можно включить принудительно, см. выше.
P.S.: при исследовании использовал JDK 8, JDK 11 и Spring Boot 2.1.1
UPDATE 2: интересно, что для для @RequestParam и @PathVariableвторая работает вторая стратегия LocalVariableTableParameterNameDiscoverer на основе информации, полученной ASM из байткода. В том числе и для обычного Spring (без Boot) и без параметра компиляции.
Annotation @param in Java
In Java, annotations are the tags that represent the metadata, which is attached with a class, interface, methods, etc., to indicate some special type of additional information that can be used by JVM and Java compiler.
The @param annotation is a special format comment used by the javadoc that generates documentation.
In this tutorial, we will discuss how does the annotation @param works in Java.
As mentioned above, @param is a special type of format comment used by the javadoc , that generates the documentation. It denotes a description of the parameter (or multiple parameters) a method may receive.
There are also annotations like @return and @see to describe the return values and related information.
If used in any method specifically, this annotation will not affect the method of its working in any way. It is just used for creating documentation of that particular method. We can put this annotation right before a class, method, field, constructor, interface, etc.
The advantage to using this annotation is that by using this, we allow the simple Java classes, which might be containing attributes and some custom javadoc tags, to serve as simple metadata descriptions for the code generation.
/* *@param number *@return integer */ public int main number(int num) // if number is less than 5, square it if(num 5) num = num * num; > else // else add the number to itself num = num + num; > return num; >
In the above example, method number() will act as the metadata for the rest of the code. Whenever the code reuses this method, IDE shows up the parameters this method accepts. In this case, one parameter is accepted, and that is an integer named num . Also, the return type of method, which in this case is int .
Related Article — Java Annotation
Copyright © 2023. All right reserved