// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= Поддержка обработчиков аннотаций в IDE NetBeans (часть II) Использование пользовательских обработчиков аннотаций в IDE
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Поддержка обработчиков аннотаций в IDE NetBeans (часть II) Использование пользовательских обработчиков аннотаций в IDE - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Поддержка обработчиков аннотаций в IDE NetBeans (часть II) Использование пользовательских обработчиков аннотаций в IDE

_Составитель:, автор и редактор: Ирина Филиппова (Irina Filippova) _


* *Использование пользовательских обработчиков аннотаций в среде IDE*

image::../../../images_www/articles/71/netbeans-stamp-71-72-73.png[title="Содержимое этой страницы применимо к IDE NetBeans 7.0, 7.1, 7.2 и 7.3"]

В этом разделе учебного курса описываются способы добавления собственного обработчика особых аннотаций в проект в среде IDE. Написание обработчика не входит в круг задач данного учебного курса. Здесь описывается добавление его к проекту IDE NetBeans.

Образец приложения, используемый в этом приложении, был создан Джесси Глик (Jesse Glick) и опубликован как link:http://wiki.netbeans.org/FaqApt[+Часто задаваемые вопросы по вводу+] для предыдущих выпусков IDE.

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

*Для работы с этим учебным курсом требуются программное обеспечение и ресурсы, перечисленные ниже.*

|===
|Программное обеспечение или материал |Требуемая версия 

|link:https://netbeans.org/downloads/index.html[+IDE NetBeans+] |7.0, 7.1, 7.2, 7.3 

|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Комплект для разработчика на языке Java (JDK)+] |версия 6 или 7 

|link:http://code.google.com/p/projectlombok/downloads/list[+lombok.jar+] |версия 1.12.4 и более поздние 
|===


== Определение аннотации и создание обработчика аннотаций

В этом упражнении мы создадим проект библиотеки классов.

1. Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Class Library ("Библиотека классов Java") в категории Java. Нажмите кнопку "Далее".
2. Введите * ``AnnProcessor`` * в поле "Имя проекта" и укажите местоположение для проекта. Нажмите кнопку "Завершить".

При нажатии кнопки "Готово" среда IDE создаст проект библиотеки классов, который появится в окне Projects ("Проекты").


[start=3]
. Щелкните правой кнопкой мыши узел проекта AnnProcessor в окне Projects ("Проекты") и выберите Properties ("Свойства").

[start=4]
. В категории "Источники" подтвердите, что для JDK 6 или JDK 7 указан формат исходного кода/двоичный формат.

[start=5]
. Выберите вкладку "Библиотеки" и подтвердите, что платформой Java является JDK 1.6 или JDK 1.7. Нажмите кнопку "ОК", чтобы закрыть окно Project Properties ("Свойства проекта").

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

1. В проекте 'AnnProcessor' щелкните правой кнопкой мыши узел 'Пакеты исходных кодов' и выберите 'Создать > Пакет Java'.
2. Введите * ``ann`` * в поле "Имя пакета" и нажмите кнопку "Готово" для создания нового пакета Java.
3. Повторите два предыдущих действия, чтобы создать пакет Java под названием * ``proc`` *.

После создания двух пакетов Java структура проекта должна быть подобной изображенной ниже.

image::images/packages.png[title="Структура проекта для обработчика аннотаций."]

[start=4]
. Щелкните правой кнопкой мыши пакет Java  ``ann``  и выберите New ("Создать") > Java class ("Класс Java").

[start=5]
. Введите * ``Handleable`` * в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить".

[start=6]
. Измените файл  ``Handleable.java`` , добавив приведенный ниже код. Сохраните файл.

[source,java]
----

package ann;

public *@interface* Handleable {

}
----

Так объявляются аннотации -- совершенно аналогично объявлению интерфейса. Различием является то, что ключевому слову  ``interface``  должен предшествовать знак  ``@`` . Эта аннотация именуется  ``Handleable``  (обрабатываемой).

*Дополнительные сведения.* В объявлениях аннотаций можно указать дополнительные параметры, например, типы элементов, к которым можно добавить аннотации, например, классы или методы. Для этого добавляется  ``@Target(value = {ElementType.TYPE})``  для классов и  ``@Target(value = {ElementType.METHOD}).``  Таким образом, объявление аннотаций становится автоматическим с помощью _мета-аннотаций_.

Теперь необходимо добавить к обработчику аннотаций код для обработки аннотации  ``Handleable`` .


[start=7]
. Щелкните правой кнопкой мыши пакет* `` Java `` proc* и выберите New ("Создать") > Java class ("Класс Java").

[start=8]
. Введите * ``HandleableProcessor`` * в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить".

[start=9]
. Измените класс  ``HandleableProcessor.java`` , добавив нижеприведенный код. Сохраните изменения.

*Примечание.* Значение  ``@SupportedSourceVersion``  (выделено *жирным шрифтом*) зависит от используемой версии JDK и может быть двух вариантов:  ``(SourceVersion.RELEASE_7)``  или  ``(SourceVersion.RELEASE_6)`` .


[source,java]
----

package proc;

import ann.Handleable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(*SourceVersion.RELEASE_7*)
public class HandleableProcessor extends AbstractProcessor {

    /** public for ServiceLoader */
    public HandleableProcessor() {
    }

    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
            if (e.getKind() != ElementKind.FIELD) {
                processingEnv.getMessager().printMessage(
                        Diagnostic.Kind.WARNING,
                        "Not a field", e);
                continue;
            }
            String name = capitalize(e.getSimpleName().toString());
            TypeElement clazz = (TypeElement) e.getEnclosingElement();
            try {
                JavaFileObject f = processingEnv.getFiler().
                        createSourceFile(clazz.getQualifiedName() + "Extras");
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
                        "Creating " + f.toUri());
                Writer w = f.openWriter();
                try {
                    PrintWriter pw = new PrintWriter(w);
                    pw.println("package "
                            + clazz.getEnclosingElement().getSimpleName() + ";");
                    pw.println("public abstract class "
                            + clazz.getSimpleName() + "Extras {");
                    pw.println("    protected " + clazz.getSimpleName()
                            + "Extras() {}");
                    TypeMirror type = e.asType();
                    pw.println("    /** Handle something. */");
                    pw.println("    protected final void handle" + name
                            + "(" + type + " value) {");
                    pw.println("        System.out.println(value);");
                    pw.println("    }");
                    pw.println("}");
                    pw.flush();
                } finally {
                    w.close();
                }
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                        x.toString());
            }
        }
        return true;
    }

    private static String capitalize(String name) {
        char[] c = name.toCharArray();
        c[0] = Character.toUpperCase(c[0]);
        return new String(c);
    }
}
----

Давайте рассмотрим внимательнее основные части кода, образующие обработчик аннотаций (обратите внимание, что для удобства рассмотрения код приведен здесь лишь частично).

Сперва следует указать типы аннотаций, поддерживаемые обработчиком аннотаций (используя  ``@SupportedAnnotationTypes`` ) и поддерживаемую версию исходных файлов (используя  ``@SupportedSourceVersion`` ). В данном случае версией является JDK 6: 


[source,java]
----

@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
----

Затем следует объявить общедоступный класс для обработчика, расширяющий класс ``AbstractProcessor``  из пакета  ``javax.annotation.processing`` .  ``AbstractProcessor``  является стандартным надклассом для обработчиков конкретных аннотаций и содержит необходимые методы для обработки аннотаций.


[source,java]
----

public class HandleableProcessor extends AbstractProcessor {
...
}
----

Теперь необходимо предоставить общедоступный конструктор для данного класса.


[source,java]
----

public class HandleableProcessor extends AbstractProcessor {
*    public HandleableProcessor() {
    }*
...

}
----

Затем следует вызвать метод  ``process`` () родительского класса  ``AbstractProcessor`` . Посредством этого метода предоставляются аннотации, доступные для обработки. Кроме того, этот метод содержит данные о цикле обработки.


[source,java]
----

public class HandleableProcessor extends AbstractProcessor {*
   *...
*     public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
     ...
     }
*
}
----

Логика обработчика аннотаций содержится внутри метода  ``process()``  класса  ``AbstractProcessor`` . Обратите внимание, что при помощи класса  ``AbstractProcessor``  также можно получить доступ к интерфейсу  ``ProcessingEnvironment`` , позволяющему обработчикам аннотаций использовать несколько полезных функций, например средство для работы с файловой системой (обработчик файловой системы, позволяющий обработчикам аннотаций создавать файлы) и средство вывода сообщений (способ предупреждения об ошибках обработчиков аннотаций).


[source,java]
----

public class HandleableProcessor extends AbstractProcessor {*
   *...
     public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {//For each element annotated with the Handleable annotation
            *for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {

*//Check if the type of the annotated element is not a field. If yes, return a warning*.
if (e.getKind() != ElementKind.FIELD) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Not a field", e);
continue;
}
            *//Define the following variables: name and clazz*.**
String name = capitalize(e.getSimpleName().toString());
TypeElement clazz = (TypeElement) e.getEnclosingElement();
*//Generate a source file with a specified class name. *
            try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Extras");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
*//Add the content to the newly generated file*.
                    try {
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ clazz.getEnclosingElement().getSimpleName() + ";");
pw.println("public abstract class "
+ clazz.getSimpleName() + "Extras {");
pw.println("    protected " + clazz.getSimpleName()
+ "Extras() {}");
TypeMirror type = e.asType();
pw.println("    /** Handle something. */");
pw.println("    protected final void handle" + name
+ "(" + type + " value) {");
pw.println("        System.out.println(value);");
pw.println("    }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}*return true;
    * }*
...
}
----

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


[source,java]
----

public class HandleableProcessor extends AbstractProcessor {*
   *...*

  private static String capitalize(String name) {
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
return new String(c);
}
*}
----

[start=10]
. Создайте проект, щелкнув правой кнопкой мыши проект  ``AnnProcessor``  и выбрав 'Сборка'.


== Работа с обработчиком аннотаций в среде IDE

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

1. Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Application ("Приложение Java") в категории Java. Нажмите кнопку "Далее".
2. На странице "Имя и расположение" введите * ``Demo`` * в поле "Имя проекта" и укажите расположение проекта.
3. Введите * ``demo.Main`` * в поле Create Main Class ("Создать главный класс"). Нажмите кнопку "Завершить".
image::images/demo-project-wizard.png[title="Создание проекта Demo в мастере создания проектов."]

[start=4]
. Откройте окно 'Свойства проекта' и убедитесь, что JDK 6 или JDK 7 выбран как двоичный формат/формат исходного кода на панели "Исходные коды", а также что JDK 1.6 or JDK 1.7 установлен как платформа Java на панели "Библиотеки".

[start=5]
. Измените класс  ``Main.java`` , добавив приведенный ниже код. Сохраните изменения.

[source,java]
----

package demo;

*import ann.Handleable;*

public class Main *extends MainExtras* {

    *@Handleable
    private String stuff;*

    *public static void main(String[] args) {
        new Main().handleStuff("hello");
    }*
}
----

Данный код содержит следующие элементы:

* оператор импорта для обработчика особых аннотаций  ``ann.Handleable`` ;
* общедоступный класс  ``Main`` , расширяющий класс  ``MainExtras``  ( ``MainExtras``  должен быть создан обработчиком аннотаций во время компиляции);
* закрытое поле под названием  ``stuff`` , с аннотацией  ``@Handleable`` ;
* метод  ``main`` , вызывающий метод  ``handleStuff`` , который объявляется в автоматически создаваемом классе  ``MainExtras`` .

В этом простом примере метод  ``handleStuff``  только распечатывает текущее значение. Назначение метода можно изменить.

После сохранения кода  ``Main.java``  можно увидеть, что среда IDE сообщает о ряде ошибок компиляции. Это происходит, поскольку обработчик аннотаций еще не добавлен в проект.


[start=6]
. Щелкните правой кнопкой мыши узел проекта  ``Demo``  в окне "Проекты", выберите "Свойства", затем выберите категорию "Библиотеки" в окне 'Свойства проекта'.

[start=7]
. На вкладке Compile ("Компиляция") щелкните Add Project ("Добавить проект") и найдите проект  ``AnnProcessor`` .
image::images/demo-properties-compile.png[title="Вкладка 'Компилировать' в категории 'Библиотеки' окна свойств проекта"]

Вкладка "Компиляция" соответствует параметру  ``-classpath``  link:http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#options[+компилятора Java+]. Поскольку обработчик аннотаций является единым файлом JAR, который содержит как определение аннотаций, так и обработчик аннотаций, его следует добавить к пути классов для проекта, которым является вкладка Compile ("Компиляция").


[start=8]
. Выберите категорию "Компиляция" в окне "Свойства проекта" и установите флажки "Разрешить обработку аннотаций" и "Разрешить обработку аннотаций в редакторе".

[start=9]
. Укажите, какой обработчик аннотаций должен быть запущен, нажав кнопку Add ("Добавить") рядом с текстовой областью обработчиков аннотаций и введя * ``proc.HandleableProcessor`` * в поле FQN ("Полностью определенное имя") обработчика аннотаций. 
image::images/demo-processor-fqn.png[title="Диалоговое окно 'FQN обработчика аннотаций'"]

Категория Compiling ("Компиляция") в окне Project Properties ("Свойства проекта") должна выглядеть, как на приведенном ниже изображении.

image::images/demo-properties-compiling.png[title="Категория &quot;Компиляция&quot; в окне &quot;Свойства проекта&quot;."]

[start=10]
. Нажмите кнопку OK в окне Properties ("Свойства").

*Примечание.* В файле  ``Main.java``  все еще могут отображаться ошибки компиляции. Это происходит, поскольку в среде IDE еще не определено местоположение файла  ``MainExtras.java`` , в котором объявляется метод  ``handleStuff`` . После первого создания проекта Demo будет создан файл  ``MainExtras.java`` . Если для проекта включено режим Compile On Save ("Компилировать при сохранении"), среда IDE компилирует проект при сохранении  ``Main.java`` .


[start=11]
. Щелкните правой кнопкой мыши проект Demo и выберите Build ("Сборка").

Если после сборки проекта взглянуть на него в окне Projects ("Проекты"), то можно будет увидеть новый узел  ``Generated Sources``  с файлом  ``demo/MainExtras.java`` .

image::images/demo-generated-sources.png[title="В окне 'Проекты' отображаются созданные источники"]

При просмотре содержимого созданного файла  ``MainExtras.java``  можно увидеть, что обработчик аннотаций создал класс  ``MainExtras``  с методом  ``handleStuff`` . Метод  ``handleStuff``  и является методом, вызываемым из аннотированного файла  ``Main.java`` .


[source,java]
----

package demo;
public abstract class MainExtras {
    protected MainExtras() {}
    /** Handle something. */
    protected final void handleStuff(java.lang.String value) {
        System.out.println(value);
    }
}
----

[start=12]
. Щелкните правой кнопкой мыши проект Demo и выберите Run ("Запустить").

При щелчке Run ("Запустить") в окне вывода можно будет увидеть следующее. Выполняется компиляция проекта Demo, и на экран выводится сообщение.

image::images/demo-run.png[title="В окне 'Проекты' отображаются созданные источники"]
link:/about/contact_form.html?to=3&subject=Feedback:%20Using%20the%20Annotation%20Processors%20Support%20in%20NetBeans%20IDE[+Отправить отзыв по этому учебному курсу+]


== Дополнительные сведения

Ознакомьтесь со следующими ресурсами для получения дополнительных сведений об аннотациях в приложениях Java:

* Документация Java SE - link:http://download.oracle.com/javase/6/docs/technotes/guides/language/annotations.html[+Аннотации+]
* Учебный курс Java SE - link:http://download.oracle.com/javase/tutorial/java/javaOO/annotations.html[+Аннотации+]
* link:http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#processing[+Компилятор Java: параметры обработки аннотаций+]
* link:http://blogs.oracle.com/darcy/[+Блог Джозефа Д. Дарси (Joseph D. Darcy)+] - полезные рекомендации от ведущего специалиста по спецификации JSR-269
