Статьи

Учебник по аннотациям Java — Руководство ULTIMATE (PDF Download)

ПРИМЕЧАНИЕ РЕДАКЦИИ: В этом посте мы представляем всеобъемлющее руководство по аннотациям Java. Аннотации в Java являются важной функцией, и каждый разработчик Java должен знать, как их использовать.

Мы предоставили множество учебных пособий здесь, на Java Code Geeks, таких как Создание собственных аннотаций Java, Учебное руководство по аннотациям Java с пользовательскими аннотациями и аннотации Java: изучены и объяснены .

Мы также опубликовали статьи об аннотациях, используемых в различных библиотеках, в том числе « Сделайте ваши аннотации Spring Security @Secured более DRY» и « Аннотации Java» и «Пример реального мира Spring» .

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

В этой статье мы собираемся объяснить, что такое аннотации Java, как они работают и что можно сделать с помощью аннотаций в Java.

Мы покажем, какие аннотации поставляются с Java из коробки, также называемые встроенными или мета-аннотациями, и какие новые функции доступны в Java 8, связанные с ними.

Наконец, мы реализуем пользовательскую аннотацию и приложение-процессор (потребитель), которое использует аннотации с использованием отражения в Java.

Мы перечислим некоторые очень известные и широко используемые библиотеки на основе аннотаций, таких как Junit, JAXB, Spring и Hibernate.

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

  • Затмение Луна 4.4
  • Обновление JRE 8.20
  • Junit 4
  • Hibernate 4.3.6
  • FindBugs 3.0.0

1. Почему аннотации?

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

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

Другие обстоятельства помогают этому решению: в этот момент XML использовался в качестве стандартного механизма конфигурации кода для приложений различного типа. Это был не лучший способ сделать это из-за развязки между кодом и XML (XML — это не код!) И будущего обслуживания этих отсоединенных приложений. Существовали и другие причины, такие как, например, использование зарезервированного слова «@deprecated» (с маленьким d) в Javadocs, начиная с обновления 4 для Java. Я очень уверен, что это было одной из причин текущего синтаксиса аннотаций с использованием «@». ».

Основные требования спецификации Java, связанные с проектированием и разработкой аннотаций:

2. Введение

Лучший способ объяснить, что такое аннотация, — это слово метаданные: данные, которые содержат информацию о себе. Аннотации представляют собой метаданные кода; они содержат информацию о самом коде.

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

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

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

3. Потребители

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

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

Например, в случае встроенных аннотаций (Meta-аннотаций), которые поставляются из коробки со стандартной Java, потребителем является виртуальная машина Java (JVM), выполняющая аннотированный код. Это другие примеры, которые мы увидим позже в этом руководстве, например, Junit, где потребитель — процессор Junit, считывающий и анализирующий аннотированные тестовые классы и определяющий, например, в зависимости от аннотации, в каком порядке будут проходить юнит-тесты. или какие методы будут выполняться до и после каждого теста.

Мы увидим это более подробно в главе, связанной с Junit.

Потребители используют отражение в Java для чтения и анализа аннотированного исходного кода. Основными пакетами, используемыми для этой цели, являются java.lang и java.lang.reflect . В этом уроке мы объясним, как создать собственного потребителя с нуля, используя отражение.

4. Синтаксис аннотаций и элементы аннотаций

Аннотация объявляется с использованием символа ‘@’ в качестве префикса имени примечания. Это указывает компилятору, что этот элемент является аннотацией. Пример:

1
2
3
4
@Annotation
public void annotatedMehod() {
...
 }

Указанная выше аннотация называется «Аннотация» и представляет собой аннотированный метод annotatedMethod() . Об этом позаботится компилятор.

Аннотация имеет элементы в виде ключей-значений. Эти «элементы» являются свойствами аннотации.

1
2
3
4
5
6
7
@Annotation(
   info = "I am an annotation",
   counter = "55"
)
public void annotatedMehod() {
...
 }

Если аннотация содержит только один элемент (или если требуется указать только один элемент, поскольку остальные имеют значения по умолчанию), мы можем сделать что-то вроде:

1
2
3
4
@Annotation("I am an annotation")
public void annotatedMehod() {
...
 }

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

Для элемента возможны несколько аннотаций, в данном случае для класса:

1
2
3
@ Annotation (info = "U a u O")
@ Annotation2
class AnnotatedClass { ... }

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

5. Где можно использовать

Аннотации могут использоваться практически в каждом элементе Java-программы: классы, поля, методы, пакеты, переменные и т. Д.

Начиная с Java 8 доступна концепция аннотаций по типу. До Java 8 аннотации должны использоваться только в объявлениях перечисленных ранее элементов. После Java 8 также в объявлении типов может использоваться аннотация. Теперь доступно что-то вроде следующего:

1
@MyAnnotation String str = "danibuiza";

Мы увидим этот механизм более подробно в главе, связанной с аннотациями Java 8.

6. Варианты использования

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

  • Информация для компилятора: аннотации могут использоваться компилятором для создания предупреждений или даже ошибок, основанных на различных правилах. Одним из примеров такого использования является аннотация Java 8 @FunctionalInterface . Это заставляет компилятор проверять аннотированный класс и проверять, является ли он правильным функциональным интерфейсом или нет.
  • Документация. Программные приложения могут использовать аннотации для измерения качества кода, например FindBugs или PMD, или автоматически генерировать отчеты, такие как Jenkins, Jira или Teamcity.
  • Генерация кода: аннотации могут использоваться для автоматической генерации кода или файлов XML с использованием информации метаданных, представленной в коде. Хорошим примером этого является библиотека JAXB.
  • Обработка во время выполнения: аннотации, которые проверяются во время выполнения, могут использоваться для различных целей, таких как модульное тестирование (Junit), внедрение зависимостей (Spring), проверка, ведение журнала (Log4J), доступ к данным (Hibernate) и т. Д.

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

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

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

Некоторые из следующих стандартных аннотаций называются мета-аннотациями; их цели — другие аннотации и содержат информацию о них:

  • @Retention : эта аннотация аннотирует другие аннотации и используется для указания того, как хранить помеченную аннотацию. Эта аннотация является своего рода мета-аннотацией, поскольку она помечает аннотацию и сообщает о ее природе. Возможные значения:
    • SOURCE : Указывает, что эта аннотация игнорируется компилятором и JVM (недоступно во время выполнения) и сохраняется только в источнике.
    • CLASS : Указывает, что аннотация будет сохранена компилятором, но проигнорирована JVM и поэтому не будет доступна во время выполнения.
    • RUNTIME : означает, что аннотация будет сохранена виртуальной машиной Java и может использоваться во время выполнения с помощью отражения.

Мы увидим несколько примеров этой аннотации в этом уроке.

  • @Target : этот ограничивает элементы, к которым может применяться аннотация. Возможен только один тип. Вот список доступных типов:
    • ANNOTATION_TYPE означает, что аннотация может быть применена к другой аннотации.
    • CONSTRUCTOR может быть применен к конструктору.
    • FIELD может применяться к полю или свойству.
    • LOCAL_VARIABLE может быть применен к локальной переменной.
    • METHOD может быть применен к аннотации уровня метода.
    • PACKAGE может быть применен к декларации пакета.
    • PARAMETER может применяться к параметрам метода.
    • TYPE может быть применен к любому элементу класса.
  • @Documented : аннотированные элементы будут задокументированы с помощью инструмента Javadoc. По умолчанию аннотации не документированы. Эта аннотация может быть применена к другой аннотации.
  • @Inherited : по умолчанию аннотации не наследуются подклассами. Эта аннотация помечает аннотацию для автоматического наследования всех подклассов, расширяющих аннотированный класс. Эта аннотация может быть применена к элементам класса.
  • @Deprecated : указывает, что аннотированный элемент не должен использоваться. Эта аннотация заставляет компилятор генерировать предупреждающее сообщение. Может применяться к методам, классам и полям. Объяснение того, почему этот элемент устарел, и при использовании этой аннотации должно быть предоставлено альтернативное использование.
  • @SuppressWarnings : указывает, что компилятор не @SuppressWarnings предупреждения по определенной причине или причинам. Например, если мы не хотим получать предупреждения из-за неиспользуемых частных методов, мы можем написать что-то вроде:
1
2
3
4
@SuppressWarnings( "unused")
private String myNotUsedMethod(){
    ...
}

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

8. Java 8 и аннотации

Java 8 имеет несколько преимуществ. Также улучшена структура аннотаций. В этой главе мы собираемся объяснить и предоставить примеры 3 основных тем, представленных в восьмом обновлении Java: аннотации @Repeatable , введения объявлений аннотаций типов и аннотации функционального интерфейса @FunctionalInterface (используется в сочетании с Lambdas).

  • @Repeatable : указывает, что аннотация, @Repeatable этим, может быть применена более одного раза к одному и тому же объявлению элемента.

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

1
2
3
4
5
6
7
8
9
/**
 * Container for the {@link CanBeRepeated} Annotation containing a list of values
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE_USE )
public @interface RepeatedValues
{
    CanBeRepeated[] value();
}

После этого мы создаем саму аннотацию и помечаем ее мета-аннотацией @Repeatable:

1
2
3
4
5
6
7
8
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE_USE )
@Repeatable( RepeatedValues.class )
public @interface CanBeRepeated
{
 
    String value();
}

Наконец, мы можем увидеть, как использовать его (несколько раз) в данном классе:

1
2
3
4
5
6
7
@CanBeRepeated( "the color is green" )
@CanBeRepeated( "the color is red" )
@CanBeRepeated( "the color is blue" )
public class RepeatableAnnotated
{
 
}

Если бы мы попытались сделать то же самое с неповторяющейся аннотацией:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE_USE )
public @interface CannotBeRepeated
{
 
    String value();
}
 
@CannotBeRepeated( "info" )
/*
 * if we try repeat the annotation we will get an error: Duplicate annotation of non-repeatable type
 *
 * @CannotBeRepeated. Only annotation types marked
 *
 * @Repeatable can be used multiple times at one target.
 */
// @CannotBeRepeated( "more info" )
public class RepeatableAnnotatedWrong
{
 
}

Мы получили бы ошибку от компилятора как:

1
Duplicate annotation of non-repeatable type
  • С Java 8 также возможно использовать аннотации внутри типов. Это где угодно, где вы можете использовать тип, включая операторы new, castings, Implements и throws. Аннотации типов позволяют улучшить анализ кода Java и могут обеспечить еще более строгую проверку типов. Следующие примеры проясняют этот момент:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
@SuppressWarnings( "unused" )
public static void main( String[] args )
{
    // type def
    @TypeAnnotated
    String cannotBeEmpty = null;
 
    // type
    List<@TypeAnnotated String> myList = new ArrayList<String>();
 
    // values
    String myString = new @TypeAnnotated String( "this is annotated in java 8" );
 
}
 
// in method params
public void methodAnnotated( @TypeAnnotated int parameter )
{
    System.out.println( "do nothing" );
}

Все это было невозможно до Java 8.

  • @FunctionalInterface : эта аннотация указывает, что аннотируемый элемент будет функциональным интерфейсом. Функциональный интерфейс — это интерфейс, который имеет только один абстрактный метод (не метод по умолчанию). Компилятор будет обрабатывать аннотированный элемент как функциональный интерфейс и выдаст ошибку, если элемент не соответствует необходимым требованиям. Вот пример функциональной аннотации интерфейса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// implementing its methods
@SuppressWarnings( "unused" )
MyCustomInterface myFuncInterface = new MyCustomInterface()
{
 
    @Override
    public int doSomething( int param )
    {
        return param * 10;
    }
};
 
// using lambdas
@SuppressWarnings( "unused" )
    MyCustomInterface myFuncInterfaceLambdas = ( x ) -> ( x * 10 );
}
 
@FunctionalInterface
interface MyCustomInterface
{
/*
 * more abstract methods will cause the interface not to be a valid functional interface and
 * the compiler will thrown an error:Invalid '@FunctionalInterface' annotation;
 * FunctionalInterfaceAnnotation.MyCustomInterface is not a functional interface
 */
    // boolean isFunctionalInterface();
 
    int doSomething( int param );
}

Эта аннотация может применяться к классам, интерфейсам, перечислениям и аннотациям, и она сохраняется JVM и доступна во время выполнения. Вот его декларация:

1
2
3
4
@Documented
 @Retention(value=RUNTIME)
 @Target(value=TYPE)
public @interface FunctionalInterface

Для получения дополнительной информации об этой аннотации http://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html .

9. Пользовательские аннотации

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

Прежде всего, определите новую аннотацию:

1
public @interface CustomAnnotationClass

Это создает новый тип аннотации под названием CustomAnnotationClass. Специальное слово, используемое для этой цели, — @interface, оно обозначает определение пользовательской аннотации.

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

Итак, мы определяем эти свойства для нашей новой пользовательской аннотации:

1
2
3
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface CustomAnnotationClass implements CustomAnnotationMethod

С помощью политики хранения RUNTIME мы указываем компилятору, что эта аннотация должна сохраняться JVM и может быть проанализирована во время выполнения с помощью отражения. С типом элемента TYPE мы указываем, что эта аннотация может быть применена к любому элементу класса.

После этого мы определим несколько свойств для этой аннотации:

01
02
03
04
05
06
07
08
09
10
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface CustomAnnotationClass
{
 
    public String author() default "danibuiza";
 
    public String date();
 
}

Выше мы только что определили автора свойства со значением по умолчанию «danibuiza» и датой свойства без значения по умолчанию. Следует отметить, что все объявления методов не могут иметь параметров и не могут иметь брошенного предложения. Типы возврата ограничены String, Class, перечислениями, аннотациями и массивами упомянутых ранее типов.

Теперь мы можем использовать нашу новую созданную пользовательскую аннотацию следующим образом:

1
2
3
4
5
@CustomAnnotationClass( date = "2014-05-05" )
public class AnnotatedClass
{
...
}

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

01
02
03
04
05
06
07
08
09
10
11
12
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
public @interface CustomAnnotationMethod
{
     
    public String author() default "danibuiza";
 
    public String date();
 
    public String description();
 
}

Этот можно использовать в объявлении метода, например:

01
02
03
04
05
06
07
08
09
10
11
@CustomAnnotationMethod( date = "2014-06-05", description = "annotated method" )
public String annotatedMethod()
    {
        return "nothing niente";
}
 
@CustomAnnotationMethod( author = "friend of mine", date = "2014-06-05", description = "annotated method" )
public String annotatedMethodFromAFriend()
{
        return "nothing niente";
}

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

10. Получение аннотаций

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

Интерфейс, который содержит все эти методы, является AnnotatedElement, и наиболее важными из них являются:

  • getAnnotations() : возвращает все аннотации для данного элемента, в том числе те, которые явно не определены в определении элемента.
  • isAnnotationPresent(annotation) : проверяет, доступна ли переданная аннотация в текущем элементе или нет.
  • getAnnotation(class) : getAnnotation(class) конкретную аннотацию, переданную в качестве параметра. Возвращает ноль, если эта аннотация отсутствует для данного элемента.

Этот класс реализуется java.lang.Class , java.lang.reflect.Method и java.lang.reflect.Field среди других, поэтому может использоваться в основном с любым видом элемента Java.

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

Мы пишем программу, которая пытается прочитать все аннотации, присутствующие в классе и его методах (мы используем для этого примера классы, определенные ранее):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main( String[] args ) throws Exception
{
 
    Class<AnnotatedClass> object = AnnotatedClass.class;
    // Retrieve all annotations from the class
    Annotation[] annotations = object.getAnnotations();
    for( Annotation annotation : annotations )
    {
        System.out.println( annotation );
    }
 
    // Checks if an annotation is present
    if( object.isAnnotationPresent( CustomAnnotationClass.class ) )
    {
 
        // Gets the desired annotation
        Annotation annotation = object.getAnnotation( CustomAnnotationClass.class );
 
        System.out.println( annotation );
 
    }
    // the same for all methods of the class
    for( Method method : object.getDeclaredMethods() )
    {
 
        if( method.isAnnotationPresent( CustomAnnotationMethod.class ) )
        {
 
            Annotation annotation = method.getAnnotation( CustomAnnotationMethod.class );
 
            System.out.println( annotation );
 
        }
 
    }
}

Результатом этой программы будет:

1
2
3
4
5
6
@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationClass(getInfo=Info, author=danibuiza, date=2014-05-05)
 
@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationClass(getInfo=Info, author=danibuiza, date=2014-05-05)
 
@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationMethod(author=friend of mine, date=2014-06-05, description=annotated method)
@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationMethod(author=danibuiza, date=2014-06-05, description=annotated method)

В приведенной выше программе мы можем увидеть использование метода getAnnotations() для извлечения всех аннотаций для данного объекта (метода или класса). Мы также показали, как проверить наличие конкретной аннотации и извлечь ее в положительном случае, используя методы isAnnotationPresent() и getAnnotation() .

11. Наследование в аннотациях

Аннотации могут использовать наследование в Java. Это наследование не имеет ничего или почти ничего общего с тем, что мы знаем по наследованию в объектно-ориентированном языке программирования, где унаследованный класс наследует методы, элементы и поведение от своего суперкласса или интерфейса.

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

Мы увидим это на примере, который прояснит ситуацию. Сначала мы определяем пользовательскую аннотацию, которая автоматически использует наследование

1
2
3
4
5
6
7
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InheritedAnnotation
{
 
}

У нас есть суперкласс AnnotatedSuperClass с аннотацией @InheritedAnnotation объявленной выше:

01
02
03
04
05
06
07
08
09
10
@InheritedAnnotation
public class AnnotatedSuperClass
{
 
    public void oneMethod()
    {
 
    }
 
}

Подкласс, расширяющий этот:

1
2
3
4
5
6
7
8
9
public class AnnotatedSubClass extends AnnotatedSuperClass
{
 
    @Override
    public void oneMethod(){
         
    }
     
}

@InheritedAnnotation AnnotatedSubClass показанный выше, автоматически наследует аннотацию @InheritedAnnotation . Это можно увидеть с помощью следующего теста с методом isAnnotationPresent() присутствующим в каждом классе:

1
2
3
System.out.println( "is true: " + AnnotatedSuperClass.class.isAnnotationPresent( InheritedAnnotation.class ) );
         
System.out.println( "is true: " + AnnotatedSubClass.class.isAnnotationPresent( InheritedAnnotation.class ) );

Вывод в эти строки:

1
2
is true: true
is true: true

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

Если мы попытаемся использовать этот вид аннотации в таком интерфейсе:

1
2
3
4
5
6
7
@InheritedAnnotation
public interface AnnotatedInterface
{
 
    public void oneMethod();
 
}

Реализация для этого:

01
02
03
04
05
06
07
08
09
10
public class AnnotatedImplementedClass implements AnnotatedInterface
{
 
    @Override
    public void oneMethod()
    {
 
    }
 
}

И мы проверяем результат наследования аннотации с помощью isAnnotationPresent() :

1
2
3
System.out.println( "is true: " + AnnotatedInterface.class.isAnnotationPresent( InheritedAnnotation.class ) );
         
System.out.println( "is true: " + AnnotatedImplementedClass.class.isAnnotationPresent( InheritedAnnotation.class ) );

Результат предыдущей программы будет

1
2
is true: true
is true: false

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

Аннотация @Inherited применима только к классам, а примечания, присутствующие в интерфейсах, не влияют на реализующие классы. То же самое происходит с методами, переменными, пакетами и т. Д. В сочетании с этой аннотацией могут использоваться только классы.
Очень хорошее объяснение можно найти в Javadoc аннотации @Inherited :
http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Inherited.html .

Аннотации не могут наследоваться от других аннотаций; если вы попытаетесь сделать это, вы получите ошибку компиляции:

1
Annotation type declaration cannot have explicit superinterfaces

12. Известные библиотеки с использованием аннотаций

В этой главе мы покажем, как очень хорошо известные библиотеки Java используют аннотации. Несколько библиотек, таких как JAXB, Spring Framework, Findbugs, Log4j, Hibernate и Junit… (список может быть бесконечным) используют их для различных целей, таких как анализ качества кода, модульное тестирование, анализ XML, внедрение зависимостей и многие другие.

В этом уроке мы собираемся показать некоторые из этих вариантов использования:

12.1. Junit

Эта структура используется для модульного тестирования в Java. Начиная с его версии 4 аннотации широко используются и являются одним из столпов дизайна Junit.

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

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

  • @Test : эта аннотация указывает Junit, что аннотированный метод должен выполняться как модульный тест. Он применим только для методов (использующих тип целевого элемента METHOD ) и сохраняется во время выполнения виртуальной машиной Java (используя политику хранения RUNTIME).
1
2
3
4
5
6
@Test
public void testMe()
{
    //test assertions
    assertEquals(1,1);
}

В приведенном выше примере мы можем увидеть, как использовать этот вид аннотации в Junit.

  • @Before : аннотация before используется для указания Junit, что помеченный метод должен выполняться перед каждым тестом. Это очень полезно для настройки методов инициализации тестового контекста. Применимо только к методам:
1
2
3
4
5
6
7
8
@Before
public void setUp()
 {
    // initializing variables
    count = 0;
    init();
 
}
  • @After : эта аннотация используется для указания процессору Junit, что все отмеченные методы должны выполняться после каждого модульного теста. Эта аннотация обычно используется для уничтожения, закрытия или завершения методов, где ресурсы очищаются и сбрасываются:
1
2
3
4
5
6
@After
public void destroy()
{
    // closing input stream
    stream.close();
}
  • @Ignore : это указывает Junit, что отмеченные методы не должны выполняться как модульный тест. Даже если они аннотированы как тест. Их следует просто игнорировать:
1
2
3
4
5
6
7
@Ignore
@Test
public void donotTestMe()
{
    count = -22;
    System.out.println( "donotTestMe(): " + count );
}

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

  • @FixMethodOrder : указывает, какой порядок выполнения следует использовать, обычно процессор Junit позаботится об этом, и порядок выполнения по умолчанию полностью неизвестен и является случайным для программиста. Эта аннотация на самом деле не рекомендуется, так как методы и тесты Junit должны быть полностью независимы друг от друга, и порядок выполнения не должен влиять на результаты. Однако существуют случаи и сценарии, в которых порядок юнит-тестов должен следовать некоторым правилам, где эта аннотация может быть очень полезной.
1
2
@FixMethodOrder( MethodSorters.NAME_ASCENDING )
public class JunitAnnotated

Существуют другие тестовые наборы и библиотеки, использующие аннотации, такие как Mockito или JMock, где аннотации используются для создания тестовых объектов и методов ожидания.

Полный список доступных аннотаций в Junit https://github.com/junit-team/junit/wiki/Getting-started

12.2. Hibernate ORM

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

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

Ниже приведен фрагмент @Entity и @Table . Они используются для указания потребителю (процессору Hibernate), что аннотированный класс является компонентом управления данными, и указывает, какую таблицу SQL следует использовать для объектов этого класса. На самом деле эта аннотация просто объясняет, какая из них является основной таблицей; Есть также аннотации для вторичного.

1
2
3
@Entity
@Table( name = "hibernate_annotated" )
public class HibernateAnnotated

В следующем фрагменте кода мы показываем, как указать процессору Hibernate, что отмеченный элемент является идентификатором таблицы с именем «id» и что он должен генерироваться автоматически (типичные идентификаторы SQL с автоматическим приращением):

1
2
3
4
@Id
@GeneratedValue
@Column( name = "id" )
private int    id;

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

1
2
@Column( name = "description" )
private String description;

Это означает, что отмеченный элемент является столбцом с именем «description» в таблице, указанной в начале класса.

Эти аннотации относятся к пакету http://docs.oracle.com/javaee/6/api/javax/persistence/package-summary.html из Java Enterprise Edition и в основном охватывают все доступные аннотации, которые используются в спящем режиме (или в наименее распространенные из них).

12.3. Spring MVC

Spring — это среда, широко используемая для реализации приложений Java Enterprise. Одна из его самых важных особенностей — использование внедрения зависимостей в программах Java.

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

Здесь мы просто покажем две из множества аннотаций, доступных в Spring. В следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
@Component
public class DependencyInjectionAnnotation
{
 
    private String description;
 
    public String getDescription()
    {
        return description;
    }
 
    @Autowired
    public void setDescription( String description )
    {
        this.description = description;
    }
 
}

В приведенном выше фрагменте мы можем найти два вида аннотаций, примененных ко всему классу и к методу соответственно:

  • @Component : указывает, что элемент, помеченный этой аннотацией, в данном случае класс, является кандидатом на автоопределение. Это означает, что аннотированный класс может быть компонентом и должен приниматься во внимание контейнером Spring.
  • @Autowired : контейнер Spring попытается выполнить автоматическую разводку byType (это своего рода сопоставление свойств с использованием типа элементов) для этого метода установки. Его также можно применять к конструктору и свойствам, а действия, выполняемые контейнером Spring в этих случаях, различны.

Для получения дополнительной информации о внедрении зависимостей и платформе Spring в целом, пожалуйста, посетите: http://projects.spring.io/spring-framework/ .

12.4. FindBugs

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

Findbugs в основном читает код (и содержащиеся в нем аннотации), используя отражение, и решает, какие действия следует предпринять в зависимости от них.

Одним из примеров является аннотация edu.umd.cs.findbugs.annotations.SuppressFBWarnings которая ожидает ключ нарушения в качестве параметра (необходимо edu.umd.cs.findbugs.annotations.SuppressFBWarnings одно или несколько значений в качестве параметра, ключ не требуется, так как это ключ по умолчанию «значение» ). Это очень похоже на один из java.lang.SuppressWarnings. Он используется, чтобы указать процессору Findbugs на игнорирование определенных нарушений при выполнении анализа кода.

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

01
02
03
04
05
06
07
08
09
10
11
@SuppressFBWarnings( "HE_EQUALS_USE_HASHCODE" )
public class FindBugsAnnotated
{
 
    @Override
    public boolean equals( Object arg0 )
    {
        return super.equals( arg0 );
    }
 
}

Приведенный выше класс переопределяет метод equals() класса Object но не делает то же самое с методом hashCode() . Обычно это проблема, потому что hashCode() и equals() должны переопределять их обоих, чтобы не было проблем при использовании элемента в качестве ключа в HashMap например. Поэтому Findbugs создаст для него запись об ошибке в отчете о нарушениях.

Если аннотации @SuppressFBWarnings со значением HE_EQUALS_USE_HASHCODE не будет, процессор FindBugs выдаст ошибку типа:

1
2
Bug: com.danibuiza.javacodegeeks.findbugsannotations.FindBugsAnnotated defines equals and uses Object.hashCode()
Bug: com.danibuiza.javacodegeeks.findbugsannotations.FindBugsAnnotated defines equals and uses Object.hashCode()

Этот класс переопределяет equals(Object) , но не переопределяет hashCode() и наследует реализацию hashCode() из java.lang.Object (который возвращает хэш-код идентификатора, произвольное значение, назначенное объекту виртуальной машиной). Следовательно, класс с большой вероятностью может нарушить инвариант, согласно которому равные объекты должны иметь одинаковые хеш-коды.

Если вы не думаете, что экземпляры этого класса будут когда-либо вставлены в HashMap / HashTable , рекомендуется использовать hashCode :

1
2
3
4
public int hashCode() {
  assert false : "hashCode not designed";
  return 42; // any arbitrary constant will do
  }
1
2
3
Rank: Troubling (14), confidence: High
Pattern: HE_EQUALS_USE_HASHCODE
Type: HE, Category: BAD_PRACTICE (Bad practice)

Эта ошибка содержит объяснение проблемы и советы о том, как ее решить. В этом случае решение в основном заключается в реализации метода hashCode() .

Полный список всех нарушений FindBugs, которые можно использовать в качестве значения в аннотации SuppressFBWarnings http://findbugs.sourceforge.net/bugDescription.html .

12,5. JAXB

JAXB — это библиотека, используемая для преобразования и отображения файлов XML в объекты Java и наоборот. На самом деле эта библиотека поставляется со стандартным JRE, и нет необходимости скачивать или настраивать ее каким-либо образом. Его можно использовать напрямую, импортировав классы из пакета javax.xml.bind.annotation в ваши приложения.

JAXB использует аннотации для информирования своего процессора (или JVM) о преобразовании XML в код (и наоборот). Например, есть аннотации, используемые для указания узлов XML в коде, атрибутов XML, значений и т. Д. Мы рассмотрим пример:

Прежде всего, мы объявляем класс, указывающий, что он должен быть узлом в XML:

1
2
3
4
5
6
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlType( propOrder = { "brand", "model", "year", "km" } )
@XmlRootElement( name = "Car" )
class Car
...

Здесь используются аннотации @XmlType и @XmlRootElement . Они информируют процессор JAXB о том, что класс Car будет узлом в XML, созданном в результате преобразования. @XmlType указывает порядок свойств в результирующем XML. JAXB выполнит правильные действия на основе этих аннотаций.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
Car car = new Car();
car.setBrand( "Mercedes" );
car.setModel( "SLK" );
car.setYear( 2011 );
car.setKm( 15000 );
 
Car carVW = new Car();
carVW.setBrand( "VW" );
carVW.setModel( "Touran" );
carVW.setYear( 2005 );
carVW.setKm( 150000 );
 
/* init jaxb marshaler */
JAXBContext jaxbContext = JAXBContext.newInstance( Car.class );
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
 
/* set this flag to true to format the output */
jaxbMarshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
 
/* marshaling of java objects in xml (output to standard output) */
jaxbMarshaller.marshal( car, System.out );
jaxbMarshaller.marshal( carVW, System.out );

Вывод этой программы будет примерно таким:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
    <brand>Mercedes</brand>
    <model>SLK</model>
    <year>2011</year>
    <km>15000</km>
</Car>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
    <brand>VW</brand>
    <model>Touran</model>
    <year>2005</year>
    <km>150000</km>
</Car>

Существует список аннотаций, которые можно использовать в JAXB для преобразования XML в Java. Более подробную информацию можно найти в https://jaxb.java.net/

13. Резюме

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

Мы перечислили встроенные аннотации, которые доступны по умолчанию в Java, некоторые из них также называются метааннотациями как @Targetили @Retention, или другие как @Overrideили @SuppressWarnings, а также новые функции, появившиеся в Java 8, связанные с аннотациями, такими как @Repeteableаннотация, @FunctionalInterfaceаннотации и аннотации по типу.

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

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

14. Загрузите исходный код учебника по аннотациям Java

Это был учебник по аннотациям Java.

15. Ресурсы

Вот список очень полезных ресурсов, связанных с аннотациями Java: