corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

Поддержка обработчиков аннотаций в среде IDE NetBeans, часть II: использование собственных обработчиков особых аннотаций в среде IDE

Составитель: Джесси Глик (Jesse Glick), автор и редактор: Ирина Филиппова (Irina Filippova)

Содержимое данной страницы применимо к среде IDE NetBeans 6.9, 7.0 b 7.1

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

Пример приложения, используемый в этом разделе, создан Джесси Гликом (Jesse Glick) и опубликован как запись часто задаваемых вопросов для предыдущих выпусков среды IDE.

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

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

Программное обеспечение или материал Требуемая версия
Среда IDE NetBeans 6.9, 7.0, 7.1
Комплект для разработчика на языке Java (JDK) Версия 6

Примечания.

  • Поддержка обработчиков особых аннотаций была добавлена к выпуску 6.9 среды IDE NetBeans. Данное руководство не будет работать с предыдущими версиями среды IDE.

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

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

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

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

  3. Щелкните правой кнопкой мыши узел проекта AnnProcessor в окне Projects ("Проекты") и выберите Properties ("Свойства").
  4. В категории Sources ("Источники") подтвердите, что для JDK 6 указан формат исходного кода/двоичный.
  5. Выберите вкладку Libraries ("Библиотеки") и подтвердите, что платформой Java является JDK 6. Нажмите кнопку "ОК", чтобы закрыть окно Project Properties ("Свойства проекта").

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

  1. Создайте пакет Java под названием ann, щелкнув правой кнопкой мыши узел Source Packages ("Пакеты исходного кода") под узлом проекта AnnProcessor и выбрав New ("Создать") > Java Package ("Пакет Java"). Введите ann в поле Package Name ("Имя пакета") и нажмите кнопку Finish ("Готово").
  2. Повторите предыдущее действие и создайте пакет Java под названием proc.

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

    снимок экрана окна Projects ("Проекты") с пакетами Java
  3. Щелкните правой кнопкой мыши пакет Java ann и выберите New ("Создать") > Java class ("Класс Java").
  4. Введите Handleable в поле Class Name ("Имя класса"). Нажмите кнопку Finish ("Готово").
  5. Измените файл Handleable.java, добавив приведенный ниже код. Сохраните файл.
    package ann;
    
    public @interface Handleable {
    
    }

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

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

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

  6. Щелкните правой кнопкой мыши пакет Java proc и выберите New ("Создать") > Java class ("Класс Java").
  7. Введите HandleableProcessor в поле Class Name ("Имя класса"). Нажмите кнопку Finish ("Готово").
  8. Измените класс 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 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 annotations,
                RoundEnvironment roundEnv) {
         ...
         }
    
    }

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

    public class HandleableProcessor extends AbstractProcessor {
       ...
         public boolean process(Set 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);
    }
    }
  9. Соберите проект, щелкнув правой кнопкой мыши проект AnnProcessor и выбрав Build ("Сборка").

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

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

  1. Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Application ("Приложение Java") в категории Java. Нажмите кнопку Next ("Далее").
  2. На странице Name and Location ("Имя и местоположение") введите Demo в поле Project Name ("Имя проекта") и укажите местоположение проекта.
  3. Введите demo.Main в поле Create Main Class ("Создать главный класс"). Нажмите кнопку Finish ("Готово").
    снимок экран мастера создания проектов
  4. Откройте окно свойств проекта и убедитесь, что JDK 6 выбран как двоичный формат/формат исходного кода на панели Sources ("Исходные коды"), а также что JDK 6 установлен как платформа Java на панели Libraries ("Библиотеки").
  5. Измените класс 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");
        }
    }

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

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

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

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

  6. Щелкните правой кнопкой мыши узел проекта Demo в окне Projects ("Проекты"), выберите Properties ("Свойства"), затем выберите категорию Libraries ("Библиотеки") в окне свойств.
  7. На вкладке Compile ("Компиляция") щелкните Add Project ("Добавить проект") и найдите проект AnnProcessor.
    снимок экрана вкладки Compile ("Компиляция") в категории Libraries ("Библиотеки") окна Properties ("Свойства") проекта

    Вкладка "Компиляция" соответствует параметру -classpath компилятора Java. Поскольку обработчик аннотаций является единым файлом JAR, который содержит как определение аннотаций, так и обработчик аннотаций, его следует добавить к пути классов для проекта, которым является вкладка Compile ("Компиляция").

  8. Выберите категорию Compiling ("Компиляция") в окне Properties ("Свойства") и установите флажки Enable Annotation Processing ("Включить обработку аннотаций") и Enable Annotation Processing in Editor ("Включить обработку аннотаций в редакторе").
  9. Укажите, какой обработчик аннотаций должен быть запущен, нажав кнопку Add ("Добавить") рядом с текстовой областью обработчиков аннотаций и введя proc.HandleableProcessor в поле FQN ("Полностью определенное имя") обработчика аннотаций.
    снимок экрана диалогового окна FQN ("Полностью определенное имя") обработчика аннотаций

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

    снимок экрана категории Compiling ("Компиляция") в окне Properties ("Свойства") проекта
  10. Нажмите кнопку OK в окне Properties ("Свойства").

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

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

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

    снимок экрана окна Projects ("Проекты") с Generated Sources

    При просмотре содержимого созданного файла 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);
        }
    }
  12. Щелкните правой кнопкой мыши проект Demo и выберите Run ("Запустить").

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

    снимок экрана окна Projects ("Проекты") с Generated Sources

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

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