
Поддержка обработчиков аннотаций в среде IDE NetBeans, часть II: использование собственных обработчиков особых аннотаций в среде IDE
Составитель: Джесси Глик (Jesse Glick), автор и редактор: Ирина Филиппова (Irina Filippova)
В этом разделе учебного курса описываются способы добавления собственного обработчика особых аннотаций в проект в среде IDE. Написание обработчика не входит в круг задач данного учебного курса. В это документе рассматриваются способы его добавления в проект среды IDE NetBeans.
Пример приложения, используемый в этом разделе, создан Джесси Гликом (Jesse Glick) и опубликован как запись часто задаваемых вопросов для предыдущих выпусков среды IDE.
Обработчик аннотаций, используемый в качестве примера, создает родительский класс для аннотированного класса. Созданный родительский класс также содержит метод, вызываемый из аннотированного класса. Следуйте указаниям по созданию и добавлению обработчика особых аннотаций в проект среды IDE, приведенным ниже.
Для работы с этим учебным курсом требуются программное обеспечение и материалы, перечисленные ниже.
Примечания.
- Поддержка обработчиков особых аннотаций была добавлена к выпуску 6.9 среды IDE NetBeans. Данное руководство не будет работать с предыдущими версиями среды IDE.
Определение аннотации и создание обработчика аннотаций
В этом упражнении мы создадим проект библиотеки классов.
- Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Class Library ("Библиотека классов Java") в категории Java. Нажмите кнопку Next ("Далее").
- Введите AnnProcessor в поле Project Name ("Имя проекта") и укажите местоположение для проекта. Нажмите кнопку Finish ("Готово").
При нажатии кнопки "Готово" среда IDE создаст проект библиотеки классов, который появится в окне Projects ("Проекты").
- Щелкните правой кнопкой мыши узел проекта AnnProcessor в окне Projects ("Проекты") и выберите Properties ("Свойства").
- В категории Sources ("Источники") подтвердите, что для JDK 6 указан формат исходного кода/двоичный.
- Выберите вкладку Libraries ("Библиотеки") и подтвердите, что платформой Java является JDK 6. Нажмите кнопку "ОК", чтобы закрыть окно Project Properties ("Свойства проекта").
В этом упражнении мы создадим несколько пакетов Java и по одному классу Java в каждом из пакетов.
- Создайте пакет Java под названием ann, щелкнув правой кнопкой мыши узел Source Packages ("Пакеты исходного кода") под узлом проекта AnnProcessor и выбрав New ("Создать") > Java Package ("Пакет Java"). Введите ann в поле Package Name ("Имя пакета") и нажмите кнопку Finish ("Готово").
- Повторите предыдущее действие и создайте пакет Java под названием proc.
После создания двух пакетов Java структура проекта должна быть подобной изображенной ниже.

- Щелкните правой кнопкой мыши пакет Java ann и выберите New ("Создать") > Java class ("Класс Java").
- Введите Handleable в поле Class Name ("Имя класса"). Нажмите кнопку Finish ("Готово").
- Измените файл Handleable.java, добавив приведенный ниже код. Сохраните файл.
package ann;
public @interface Handleable {
}
Так объявляются аннотации -- совершенно аналогично объявлению интерфейса. Различием является то, что ключевому слову interface должен предшествовать знак @. Эта аннотация именуется Handleable (обрабатываемой).
Дополнительные сведения. В объявлении аннотации также можно указать дополнительные параметры, например, к каким типам элементов (т. е. классам или методам) можно добавлять аннотации. Для этого следует добавить в объявление @Target(value = {ElementType.TYPE}) для классов и @Target(value = {ElementType.METHOD}). В результате этого к самой аннотации добавляются метааннотации.
Теперь необходимо добавить к обработчику аннотаций код для обработки аннотации Handleable.
- Щелкните правой кнопкой мыши пакет Java proc и выберите New ("Создать") > Java class ("Класс Java").
- Введите HandleableProcessor в поле Class Name ("Имя класса"). Нажмите кнопку Finish ("Готово").
- Измените класс HandleableProcessor.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_6)
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:
@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
Затем следует объявить общедоступный класс для обработчика, расширяющий классAbstractProcessor из пакета javax.annotation.processing. AbstractProcessor является стандартным надклассом для обработчиков конкретных аннотаций и содержит необходимые методы для обработки аннотаций.
public class HandleableProcessor extends AbstractProcessor {
...
}
Теперь необходимо предоставить общедоступный конструктор для данного класса.
public class HandleableProcessor extends AbstractProcessor {
public HandleableProcessor() {
}
...
}
Затем следует вызвать метод process() родительского класса AbstractProcessor. Посредством этого метода предоставляются аннотации, доступные для обработки. Кроме того, этот метод содержит данные о цикле обработки.
public class HandleableProcessor extends AbstractProcessor {
...
public boolean process(Set extends TypeElement> annotations,
RoundEnvironment roundEnv) {
...
}
}
Логика обработчика аннотаций содержится внутри метода process() класса AbstractProcessor. Обратите внимание, что при помощи класса AbstractProcessor также можно получить доступ к интерфейсу ProcessingEnvironment, позволяющему обработчикам аннотаций использовать несколько полезных функций, например средство для работы с файловой системой (обработчик файловой системы, позволяющий обработчикам аннотаций создавать файлы) и средство вывода сообщений (способ предупреждения об ошибках обработчиков аннотаций).
public class HandleableProcessor extends AbstractProcessor {
...
public boolean process(Set extends TypeElement> annotations,
RoundEnvironment roundEnv) {
//Для каждого объявленного при помощи аннотации Handleable элемента
for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
//Проверка, не является ли тип аннотированного элемента полем. При положительном результате возвращается предупреждение. if (e.getKind() != ElementKind.FIELD) { processingEnv.getMessager().printMessage( Diagnostic.Kind.WARNING, "Not a field", e); continue; }
//Определение следующих переменных: name и clazz. 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;
}
...
}
В последнем блоке этого кода объявляется метод capitalize, используемый для написания имени аннотированного элемента с заглавной буквы.
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); }
}
- Соберите проект, щелкнув правой кнопкой мыши проект AnnProcessor и выбрав Build ("Сборка").
Работа с обработчиком аннотаций в среде IDE
В этом разделе мы создадим приложение Java, в котором будет использоваться обработчик аннотаций.
- Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Application ("Приложение Java") в категории Java. Нажмите кнопку Next ("Далее").
- На странице Name and Location ("Имя и местоположение") введите Demo в поле Project Name ("Имя проекта") и укажите местоположение проекта.
- Введите demo.Main в поле Create Main Class ("Создать главный класс"). Нажмите кнопку Finish ("Готово").

- Откройте окно свойств проекта и убедитесь, что JDK 6 выбран как двоичный формат/формат исходного кода на панели Sources ("Исходные коды"), а также что JDK 6 установлен как платформа Java на панели Libraries ("Библиотеки").
- Измените класс Main.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");
}
}
Данный код содержит следующие элементы:
После сохранения кода Main.java можно увидеть, что среда IDE сообщает о ряде ошибок компиляции. Это происходит, поскольку обработчик аннотаций еще не добавлен в проект.
- Щелкните правой кнопкой мыши узел проекта Demo в окне Projects ("Проекты"), выберите Properties ("Свойства"), затем выберите категорию Libraries ("Библиотеки") в окне свойств.
- На вкладке Compile ("Компиляция") щелкните Add Project ("Добавить проект") и найдите проект AnnProcessor.
Вкладка "Компиляция" соответствует параметру -classpath компилятора Java. Поскольку обработчик аннотаций является единым файлом JAR, который содержит как определение аннотаций, так и обработчик аннотаций, его следует добавить к пути классов для проекта, которым является вкладка Compile ("Компиляция").
- Выберите категорию Compiling ("Компиляция") в окне Properties ("Свойства") и установите флажки Enable Annotation Processing ("Включить обработку аннотаций") и Enable Annotation Processing in Editor ("Включить обработку аннотаций в редакторе").
- Укажите, какой обработчик аннотаций должен быть запущен, нажав кнопку Add ("Добавить") рядом с текстовой областью обработчиков аннотаций и введя proc.HandleableProcessor в поле FQN ("Полностью определенное имя") обработчика аннотаций.
Категория Compiling ("Компиляция") в окне Properties ("Свойства") должна выглядеть, как на приведенном ниже изображении.
- Нажмите кнопку OK в окне Properties ("Свойства").
Примечание. В файле Main.java могут по-прежнему обнаруживаться ошибки компиляции. Это происходит, поскольку в среде IDE еще не определено местоположение файла MainExtras.java, в котором объявляется метод handleStuff. После первого создания проекта Demo будет создан файл MainExtras.java. Если для проекта включено режим Compile On Save ("Компилировать при сохранении"), среда IDE компилирует проект при сохранении Main.java.
- Щелкните правой кнопкой мыши проект Demo и выберите Build ("Сборка").
Если после сборки проекта взглянуть на него в окне Projects ("Проекты"), то можно будет увидеть новый узел Generated Sources с файлом demo/MainExtras.java.
При просмотре содержимого созданного файла MainExtras.java можно увидеть, что обработчик аннотаций создал класс MainExtras с методом handleStuff. Метод handleStuff и является методом, вызываемым из аннотированного файла Main.java.
package demo;
public abstract class MainExtras {
protected MainExtras() {}
/** Handle something. */
protected final void handleStuff(java.lang.String value) {
System.out.println(value);
}
}
- Щелкните правой кнопкой мыши проект Demo и выберите Run ("Запустить").
При щелчке Run ("Запустить") в окне вывода можно будет увидеть следующее. Выполняется компиляция проекта Demo, и на экран выводится сообщение.
Дополнительные сведения
Ознакомьтесь со следующими ресурсами для получения дополнительных сведений об аннотациях в приложениях Java:
|
|