| // |
| // 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. |
| // |
| |
| = NetBeans IDE 中的标注处理程序支持,第二部分:在 IDE 中使用自己的定制标注处理程序 |
| :jbake-type: tutorial |
| :jbake-tags: tutorials |
| :jbake-status: published |
| :icons: font |
| :syntax: true |
| :source-highlighter: pygments |
| :toc: left |
| :toc-title: |
| :description: NetBeans IDE 中的标注处理程序支持,第二部分:在 IDE 中使用自己的定制标注处理程序 - Apache NetBeans |
| :keywords: Apache NetBeans, Tutorials, NetBeans IDE 中的标注处理程序支持,第二部分:在 IDE 中使用自己的定制标注处理程序 |
| |
| _编写人:Jesse Glick,编写和维护人:Irina Filippova_ |
| |
| |
| * *在 IDE 中使用自己的定制标注处理程序* |
| |
| image::../../../images_www/articles/71/netbeans-stamp-71-72-73.png[title="此页上的内容适用于 NetBeans IDE 7.0、7.1、7.2 和 7.3"] |
| |
| 在本教程的此部分,您会了解到如何在 IDE 中将自创的定制标注处理程序添加到项目中。本教程的目的并不在于教授如何编写标注处理程序。它解释如何将其添加到 NetBeans IDE 项目中。 |
| |
| 此部分使用的样例应用程序由 Jesse Glick 创建,并作为早期发行版本的 IDE 的link:http://wiki.netbeans.org/FaqApt[+常见问题解答条目+]发布。 |
| |
| 用作示例的标注处理程序为标注类生成父类。生成的父类还包含从标注类中调用的方法。请按照下面有关如何创建定制标注处理程序并将其添加到 IDE 项目的说明进行操作。 |
| |
| *要学完本教程,您需要具备以下软件和资源。* |
| |
| |=== |
| |软件或资源 |要求的版本 |
| |
| |link:https://netbeans.org/downloads/index.html[+NetBeans IDE+] |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+] |v1.12.4 或更新版本 |
| |=== |
| |
| |
| == 定义标注与创建标注处理程序 |
| |
| 在本练习中,将创建类库项目。 |
| |
| 1. 选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Class Library"(Java 类库)项目类型。单击 "Next"(下一步)。 |
| 2. 键入 * ``AnnProcessor`` * 作为项目名称,并为项目指定位置。单击 "Finish"(完成)。 |
| |
| 单击 "Finish"(完成),此时 IDE 将创建类库项目,并在 "Projects"(项目)窗口中列出该项目。 |
| |
| |
| |
| . 在 "Projects"(项目)窗口中,右键单击 AnnProcessor 项目节点,然后选择 "Properties"(属性)。 |
| |
| |
| . 在 "Sources"(源)类别中,确认将 JDK 6 或 JDK 7 指定为源代码/二进制格式。 |
| |
| |
| . 选择 "Libraries"(库)标签,然后确认将 Java 平台设置为 "JDK 1.6" 或 "JDK 1.7"。单击 "OK"(确定),以关闭 "Project Properties"(项目属性)窗口。 |
| |
| 在本练习中,将创建两个 Java 包,并在每个包中创建一个 Java 类。 |
| |
| 1. 右键单击 "AnnProcessor" 项目下的 "Source Packages"(源包)节点,然后选择 "New"(新建)> "Java Package"(Java 包)。 |
| 2. 为 "Package Name"(包名)键入 * ``ann`` *,然后单击 "Finish"(完成)创建新的 Java 包。 |
| 3. 重复执行前面的两个步骤,以创建一个名为 * ``proc`` * 的 Java 包。 |
| |
| 创建了这两个 Java 包后,项目结构应类似于下图。 |
| |
| image::images/packages.png[title="标注处理程序项目的结构。"] |
| |
| |
| . 右键单击 ``ann`` Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。 |
| |
| |
| . 键入 * ``Handleable`` * 作为类名。单击 "Finish"(完成)。 |
| |
| |
| . 修改新的 ``Handleable.java`` 文件,进行如下更改。保存该文件。 |
| |
| [source,java] |
| ---- |
| |
| package ann; |
| |
| public *@interface* Handleable { |
| |
| } |
| ---- |
| |
| 这是一种标注声明方式,与接口声明十分类似。区别在于 ``at`` 符号 (@) 必须置于 ``interface`` 关键字之前。此标注名为 ``Handleable`` 。 |
| |
| *其他信息。*在标注声明中,还可以指定其他参数,例如,可以标注哪些类型的元素(类或方法等)。可以通过为类添加 ``@Target(value = {ElementType.TYPE})`` 和添加 ``@Target(value = {ElementType.METHOD})`` 来实现此目的。因此,标注声明使用 _meta-annotations_ 标注自身。 |
| |
| 现在,您需要为标注处理程序添加处理 ``Handleable`` 标注的代码。 |
| |
| |
| |
| . 右键单击 * ``proc`` * Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。 |
| |
| |
| . 键入 * ``HandleableProcessor`` * 作为类名。单击 "Finish"(完成)。 |
| |
| |
| . 修改 ``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) |
| ---- |
| |
| 然后,为处理程序声明一个公共类,以扩展 ``javax.annotation.processing`` 包中的 ``AbstractProcessor`` 类。 ``AbstractProcessor`` 是具体标注处理程序的标准超类,它包含处理标注所需的方法。 |
| |
| |
| [source,java] |
| ---- |
| |
| public class HandleableProcessor extends AbstractProcessor { |
| ... |
| } |
| ---- |
| |
| 现在,您需要为该类提供一个公共构造函数。 |
| |
| |
| [source,java] |
| ---- |
| |
| public class HandleableProcessor extends AbstractProcessor { |
| * public HandleableProcessor() { |
| }* |
| ... |
| |
| } |
| ---- |
| |
| 然后,调用父 ``AbstractProcessor`` 类的 ``process()`` 方法。通过此方法,提供可用于处理的标注。此外,此方法包含有关处理舍入的信息。 |
| |
| |
| [source,java] |
| ---- |
| |
| public class HandleableProcessor extends AbstractProcessor {* |
| *... |
| * public boolean process(Set<? extends TypeElement> annotations, |
| RoundEnvironment roundEnv) { |
| ... |
| } |
| * |
| } |
| ---- |
| |
| 标注处理程序的逻辑包含在 ``AbstractProcessor`` 类的 ``process()`` 方法中。注:通过 ``AbstractProcessor`` ,还可以访问 ``ProcessingEnvironment`` 接口,该接口允许标注处理程序使用多个有用的工具,如 Filer(使标注处理程序可以创建新文件的 Filer 处理程序)和 Messager(标注处理程序报告错误的一种方式)。 |
| |
| |
| [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); |
| } |
| *} |
| ---- |
| |
| |
| . 右键单击 ``AnnProcessor`` 项目,然后选择 "Build"(构建)以构建项目。 |
| |
| |
| == 在 IDE 中使用标注处理程序 |
| |
| 在本部分中,将创建一个 Java 应用程序项目,以便在其中使用标注处理程序。 |
| |
| 1. 选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Application"(Java 应用程序)项目类型。单击 "Next"(下一步)。 |
| 2. 在 "Name and Location"(名称和位置)页中,键入 * ``Demo`` * 作为项目名称,并指定项目位置。 |
| 3. 在 "Create Main Class"(创建主类)字段中,键入 * ``demo.Main`` *。单击 "Finish"(完成)。 |
| |
| image::images/demo-project-wizard.png[title="在新建项目向导中创建 "Demo"(演示)项目。"] |
| |
| |
| . 打开 "Project Properties"(项目属性)窗口,确认在 "Sources"(源)面板中选择 "JDK 6" 或 "JDK 7" 作为源代码/二进制格式,然后确认在 "Libraries"(库)面板中将 Java 平台设置为 "JDK 1.6" 或 "JDK 1.7"。 |
| |
| |
| . 修改 ``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`` 的 import 语句 |
| * 扩展 ``MainExtras`` 类( ``MainExtras`` 应由标注处理程序在编译期间生成)的公共类 ``Main`` |
| * 一个名为 ``stuff`` 的私有字段,它使用 ``@Handleable`` 标注进行标注 |
| * 调用 ``handleStuff`` 方法的 ``main`` 方法,后者在自动生成的 ``MainExtras`` 类中声明 |
| |
| 在这个简单示例中, ``handleStuff`` 方法仅输出当前值。可以修改此方法以执行其他任务。 |
| |
| 保存 ``Main.java`` 代码后,您会看到 IDE 报告多个编译错误。这是由于标注处理程序尚未添加到项目中。 |
| |
| |
| |
| . 在 "Projects"(项目)窗口中,右键单击 ``Demo`` 项目节点,选择 "Properties"(属性),然后在 "Project Properties"(项目属性)窗口中选择 "Libraries"(库)类别。 |
| |
| |
| . 在 "Compile"(编译)标签中,单击 "Add Project"(添加项目),然后找到 ``AnnProcessor`` 项目。 |
| |
| image::images/demo-properties-compile.png[title="项目的 "Properties"(属性)窗口 "Libraries"(库)类别中的 "Compile"(编译)标签"] |
| |
| "Compile"(编译)标签对应于 link:http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#options[+Java 编译器+]的 ``-classpath`` 选项。由于标注处理程序是包含标注定义和标注处理程序的单一 JAR 文件,因此,应在 "Compile"(编译)标签中将其添加到项目的类路径中。 |
| |
| |
| |
| . 在 "Project Properties"(项目属性)窗口中选择 "Compiling"(编译)类别,然后选中 "Enable Annotation Processing"(启用标注处理)和 "Enable Annotation Processing in Editor"(在编辑器中启用标注处理)复选框。 |
| |
| |
| . 单击 "Annotation Processors"(标注处理程序)文本区域旁边的 "Add"(添加)按钮,然后在 "Annotation Processor FQN"(标注处理程序 FQN)字段中键入 * ``proc.HandleableProcessor`` * 以指定要运行的标注处理程序。 |
| |
| image::images/demo-processor-fqn.png[title=""Annotation Processor FQN"(标注处理程序 FQN)对话框"] |
| |
| "Project Properties"(项目属性)窗口中的 "Compiling"(编译)类别应如下图所示。 |
| |
| image::images/demo-properties-compiling.png[title="项目 "Properties"(项目属性)窗口中的 "Compiling"(编译)类别"] |
| |
| |
| . 在 "Properties"(属性)窗口中单击 "OK"(确定)。 |
| |
| *注:*在 ``Main.java`` 文件中,仍可能会看到一些编译错误。这是由于 IDE 仍然找不到声明 ``handleStuff`` 方法的 ``MainExtras.java`` 文件。首次构建 Demo 项目后,将构建 ``MainExtras.java`` 文件。如果为项目启用了 "Compile On Save"(在保存时编译)功能,则在保存 ``Main.java`` 时,IDE 将编译项目。 |
| |
| |
| |
| . 右键单击 "Demo" 项目,然后选择 "Build"(构建)。 |
| |
| 在构建项目后,如果在 "Projects"(项目)窗口中查看该项目,则可以看到包含 ``demo/MainExtras.java`` 文件的新 ``Generated Sources`` (构建的源文件)节点。 |
| |
| image::images/demo-generated-sources.png[title="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口"] |
| |
| 如果您查看生成的 ``MainExtras.java`` 文件内容,则可以看到标注处理程序生成了包含 ``handleStuff`` 方法的 ``MainExtras`` 类。 ``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); |
| } |
| } |
| ---- |
| |
| |
| . 右键单击 "Demo" 项目,然后选择 "Run"(运行)。 |
| |
| 单击 "Run"(运行)时,"Output"(输出)窗口中应显示以下内容。Demo 项目编译并打印该信息。 |
| |
| image::images/demo-run.png[title="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口"] |
| |
| 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 规范负责人的有用提示 |