blob: 1f5d553b6f90be630f32294a2f3550a0587835eb [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!--
Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
-->
<html>
<head>
<title>NetBeans IDE 中的标注处理程序支持</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<link rel="stylesheet" type="text/css" href="../../../netbeans.css">
<meta name="keywords" content="NETBEANS, TUTORIAL, GUIDE, USER, DOCUMENTATION">
<meta name="description" content="A very simple and quick introduction to the NetBeans IDE workflow by walking you through the creation of a
simple Hello World Java console application." >
</head>
<body>
<h1>NetBeans IDE 中的标注处理程序支持,第二部分:在 IDE 中使用自己的定制标注处理程序 </h1>
<p><em>编写人:Jesse Glick,编写和维护人:Irina Filippova</em></p>
<div class="margin-around">
<div class="feedback-box margin-around float-left" style="margin-right:15px">
<p><b>目录</b></p>
<ul class="toc">
<li><a href="annotations.html">简介</a></li>
<li><a href="annotations.html#map">javac 选项和 IDE 命令的映射</a> </li>
<li><a href="annotations-lombok.html">使用支持定制标注的 Project Lombok</a></li>
<li><strong>在 IDE 中使用自己的定制标注处理程序</strong>
<ul>
<li><a href="#defineann">定义标注与创建标注处理程序</a></li>
<li><a href="#useprocessor">使用标注处理程序</a> </li>
</ul>
</li>
<li><a href="#seealso" title="另请参见"><strong>另请参见 </strong></a></li>
</ul>
</div>
</div>
<img alt="此页上的内容适用于 NetBeans IDE 6.9、7.0 和 7.1" class="stamp" src="../../../images_www/articles/71/netbeans-stamp-71-72-73.png" title="此页上的内容适用于 NetBeans IDE 6.9、7.0 和 7.1" width="114" />
<p>在本教程的此部分,您会了解到如何在 IDE 中将自创的定制标注处理程序添加到项目中。本教程的目的并不在于教授如何编写标注处理程序。它解释如何将其添加到 NetBeans IDE 项目中。 </p>
<p>此部分使用的样例应用程序由 Jesse Glick 创建,并作为早期发行版本的 IDE 的<a href="http://wiki.netbeans.org/FaqApt" target="_blank">常见问题解答条目</a>发布。 </p>
<p>用作示例的标注处理程序为标注类生成父类。生成的父类还包含从标注类中调用的方法。请按照下面有关如何创建定制标注处理程序并将其添加到 IDE 项目的说明进行操作。 </p>
<p style="clear:both"><b>要学完本教程,您需要具备以下软件和资源。</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">软件或资源</th>
<th class="tblheader" scope="col">要求的版本</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html" target="_blank">NetBeans IDE</a></td>
<td class="tbltd1">7.1, 7.2, 7.3 </td>
</tr>
<tr>
<td class="tbltd1"><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">Java 开发工具包 (JDK)</a></td>
<td class="tbltd1">版本 6</td>
</tr>
</tbody>
</table>
<p><strong class="notes">注:</strong></p>
<ul>
<li>NetBeans IDE 6.9 发行版中增加了对定制标注处理程序的支持。本教程不适用于早期版本的 IDE。 </li>
</ul>
<h2><a name="defineann"></a>定义标注与创建标注处理程序</h2>
<p>在本练习中,将创建类库项目。</p>
<ol>
<li>选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Class Library"(Java 类库)项目类型。单击 "Next"(下一步)。</li>
<li>键入 <tt>AnnProcessor</tt> 作为项目名称,并为项目指定位置。单击 "Finish"(完成)。
<p>单击 "Finish"(完成),此时 IDE 将创建类库项目,并在 "Projects"(项目)窗口中列出该项目。</p>
</li>
<li>在 "Projects"(项目)窗口中,右键单击 AnnProcessor 项目节点,然后选择 "Properties"(属性)。</li>
<li>在 "Sources"(源)类别中,确认将 JDK 6 指定为源代码/二进制格式。</li>
<li>选择 "Libraries"(库)标签,然后确认将 Java 平台设置为 "JDK 6"。单击 "OK"(确定),以关闭 "Project Properties"(项目属性)窗口。</li>
</ol>
<p>在本练习中,将创建两个 Java 包,并在每个包中创建一个 Java 类。</p>
<ol>
<li>右键单击 AnnProcessor 项目节点下面的 "Source Packages"(源包)节点,然后选择 "New"(新建)> "Java Package"(Java 包),以创建一个名为 <strong><tt>ann</tt></strong> 的 Java 包。键入 <tt>ann</tt> 作为包名,然后单击 "Finish"(完成)。</li>
<li>重复执行上一步,以创建一个名为 <strong><tt>proc</tt></strong> 的 Java 包。
<p>创建了这两个 Java 包后,项目结构应类似于下图。</p>
<img alt="显示 Java 包的 "Projects"(项目)窗口的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/packages.png" title="标注处理程序项目的结构。"></li>
<li>右键单击 <tt>ann</tt> Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。</li>
<li>键入 <strong><tt>Handleable</tt></strong> 作为类名。单击 "Finish"(完成)。</li>
<li>修改新的 <tt>Handleable.java</tt> 文件,进行如下更改。保存该文件。
<pre>package ann;
public <strong>@interface</strong> Handleable {
}</pre>
<p>这是一种标注声明方式,与接口声明十分类似。区别在于 <tt>at</tt> 符号 (@) 必须置于 <tt>interface</tt> 关键字之前。此标注名为 <tt>Handleable</tt></p>
<p class="notes"><strong>其他信息。</strong>在标注声明中,还可以指定其他参数,例如,可以标注哪些类型的元素(类或方法等)。可以通过为类添加 <tt>@Target(value = {ElementType.TYPE})</tt> 和添加 <tt>@Target(value = {ElementType.METHOD})</tt> 来实现此目的。因此,标注声明使用 <em>meta-annotations</em> 标注自身。 </p>
<p>现在,您需要为标注处理程序添加处理 <tt>Handleable</tt> 标注的代码。<p>
</li>
<li>右键单击 <strong><tt>proc</tt></strong> Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。</li>
<li>键入 <strong><tt>HandleableProcessor</tt></strong> 作为类名。单击 "Finish"(完成)。</li>
<li>修改 <tt>HandleableProcessor.java</tt> 类,以添加如下代码。保存所做的更改。
<pre class="examplecode">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);
}
}</pre>
<p>让我们详细了解构成标注处理程序代码的主要部分(请注意,为方便起见,我们仅提供了部分代码)。 </p>
<p>首先,您指定标注处理程序支持的标注类型(通过 <tt>@SupportedAnnotationTypes</tt>)以及支持的源文件版本(通过 <tt>@SupportedSourceVersion</tt>);在本示例中,版本为 JDK 6: <br>
<pre class="examplecode">
@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)</pre>
<p>然后,为处理程序声明一个公共类,以扩展 <tt>javax.annotation.processing</tt> 包中的 <tt>AbstractProcessor</tt> 类。<tt>AbstractProcessor</tt> 是具体标注处理程序的标准超类,它包含处理标注所需的方法。</p>
<pre class="examplecode">public class HandleableProcessor extends AbstractProcessor {
...
}</pre>
<p>现在,您需要为该类提供一个公共构造函数。</p>
<pre class="examplecode">public class HandleableProcessor extends AbstractProcessor {
<strong> public HandleableProcessor() {
}</strong>
...
}</pre>
<p>然后,调用父 <tt>AbstractProcessor</tt> 类的 <tt>process()</tt> 方法。通过此方法,提供可用于处理的标注。此外,此方法包含有关处理舍入的信息。</p>
<pre class="examplecode">public class HandleableProcessor extends AbstractProcessor {<strong>
</strong>...
<strong> public boolean process(Set annotations,
RoundEnvironment roundEnv) {
...
}
</strong>
}</pre>
<p>标注处理程序的逻辑包含在 <tt>AbstractProcessor</tt> 类的 <tt>process()</tt> 方法中。注:通过 <tt>AbstractProcessor</tt>,还可以访问 <tt>ProcessingEnvironment</tt> 接口,该接口允许标注处理程序使用多个有用的工具,如 Filer(使标注处理程序可以创建新文件的 Filer 处理程序)和 Messager(标注处理程序报告错误的一种方式)。</p>
<pre class="examplecode">public class HandleableProcessor extends AbstractProcessor {<strong>
</strong>...
public boolean process(Set annotations,
RoundEnvironment roundEnv) {<br>
//For each element annotated with the Handleable annotation
<strong>for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
<br> </strong>//Check if the type of the annotated element is not a field. If yes, return a warning<strong>.<br> if (e.getKind() != ElementKind.FIELD) {<br> processingEnv.getMessager().printMessage(<br> Diagnostic.Kind.WARNING,<br> &quot;Not a field&quot;, e);<br> continue;<br> }
</strong>//Define the following variables: name and clazz<strong>.</strong><strong><br> String name = capitalize(e.getSimpleName().toString());<br> TypeElement clazz = (TypeElement) e.getEnclosingElement();<br> </strong>//Generate a source file with a specified class name. <strong>
try {<br> JavaFileObject f = processingEnv.getFiler().<br> createSourceFile(clazz.getQualifiedName() + &quot;Extras&quot;);<br> processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,<br> &quot;Creating &quot; + f.toUri());<br> Writer w = f.openWriter();<br> </strong>//Add the content to the newly generated file<strong>.
try {<br> PrintWriter pw = new PrintWriter(w);<br> pw.println(&quot;package &quot;<br> + clazz.getEnclosingElement().getSimpleName() + &quot;;&quot;);<br> pw.println(&quot;public abstract class &quot;<br> + clazz.getSimpleName() + &quot;Extras {&quot;);<br> pw.println(&quot; protected &quot; + clazz.getSimpleName()<br> + &quot;Extras() {}&quot;);<br> TypeMirror type = e.asType();<br> pw.println(&quot; /** Handle something. */&quot;);<br> pw.println(&quot; protected final void handle&quot; + name<br> + &quot;(&quot; + type + &quot; value) {&quot;);<br> pw.println(&quot; System.out.println(value);&quot;);<br> pw.println(&quot; }&quot;);<br> pw.println(&quot;}&quot;);<br> pw.flush();<br> } finally {<br> w.close();<br> }<br> } catch (IOException x) {<br> processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,<br> x.toString());<br> }<br> }</strong><br> return true;
<strong> }</strong>
...
}</pre>
<p>此代码的最后一段代码块声明了 <tt>capitalize</tt> 方法,该方法用于大写标注的元素的名称。</p>
<pre class="examplecode">public class HandleableProcessor extends AbstractProcessor {<strong>
</strong>...<strong>
private static String capitalize(String name) {<br> char[] c = name.toCharArray();<br> c[0] = Character.toUpperCase(c[0]);<br> return new String(c);<br> }
</strong>}</pre>
</li>
<li>右键单击 <tt>AnnProcessor</tt> 项目,然后选择 "Build"(构建)以构建项目。</li>
</ol>
<h2><a name="useprocessor"></a>在 IDE 中使用标注处理程序</h2>
<p>在本部分中,将创建一个 Java 应用程序项目,以便在其中使用标注处理程序。</p>
<ol>
<li>选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Application"(Java 应用程序)项目类型。单击 "Next"(下一步)。</li>
<li>在 "Name and Location"(名称和位置)页上,键入 <tt>Demo</tt> 作为项目名称,并指定项目位置。</li>
<li>在 "Create Main Class"(创建主类)字段中,键入 <strong><tt>demo.Main</tt></strong>。单击 "Finish"(完成)。<br> <img alt="新建项目向导的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-project-wizard.png" title="在新建项目向导中创建 "Demo"(演示)项目。"></li>
<li>打开项目的 "Properties"(属性)窗口,确认在 "Sources"(源)面板中选择 "JDK 6" 作为源代码/二进制格式,然后确认在 "Libraries"(库)面板中将 Java 平台设置为 "JDK 6"。</li>
<li>修改 <tt>Main.java</tt> 类,以添加如下代码。保存所做的更改。
<pre class="examplecode">package demo;
<strong>import ann.Handleable;</strong>
public class Main <strong>extends MainExtras</strong> {
<strong>@Handleable
private String stuff;</strong>
<strong>public static void main(String[] args) {
new Main().handleStuff("hello");
}</strong>
}</pre>
<p>此代码包含以下元素:</p>
<ul>
<li>定制标注处理程序 <tt>ann.Handleable</tt> 的 import 语句</li>
<li>扩展 <tt>MainExtras</tt> 类(<tt>MainExtras</tt> 应由标注处理程序在编译期间生成)的公共类 <tt>Main</tt></li>
<li>一个名为 <tt>stuff</tt> 的私有字段,它使用 <tt>@Handleable</tt> 标注进行标注 </li>
<li>调用 <tt>handleStuff</tt> 方法的 <tt>main</tt> 方法,后者在自动生成的 <tt>MainExtras</tt> 类中声明
<p>在这个简单示例中,<tt>handleStuff</tt> 方法仅输出当前值。可以修改此方法以执行其他任务。</p></li>
</ul>
<p>保存 <tt>Main.java</tt> 代码后,您会看到 IDE 报告多个编译错误。这是由于标注处理程序尚未添加到项目中。</p>
</li>
<li>在 "Projects"(项目)窗口中,右键单击 <tt>Demo</tt> 项目节点,选择 "Properties"(属性),然后在 "Properties"(属性)窗口中选择 "Libraries"(库)类别。</li>
<li>在 "Compile"(编译)标签中,单击 "Add Project"(添加项目),然后找到 <tt>AnnProcessor</tt> 项目。<br> <img alt="项目的 "Properties"(属性)窗口 "Libraries"(库)类别中的 "Compile"(编译)标签的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-properties-compile.png" title="项目的 "Properties"(属性)窗口 "Libraries"(库)类别中的 "Compile"(编译)标签">
<p>"Compile"(编译)标签对应于 <a href="http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#options" target="_blank">Java 编译器</a><tt>-classpath</tt> 选项。由于标注处理程序是包含标注定义和标注处理程序的单一 JAR 文件,因此,应在 "Compile"(编译)标签中将其添加到项目的类路径中。</p>
</li>
<li>在 "Properties"(属性)窗口中选择 "Compiling"(编译)类别,然后选中 "Enable Annotation Processing"(启用标注处理)和 "Enable Annotation Processing in Editor"(在编辑器中启用标注处理)复选框。</li>
<li>单击 "Annotation Processors"(标注处理程序)文本区域旁边的 "Add"(添加)按钮,然后在 "Annotation Processor FQN"(标注处理程序 FQN)字段中键入 <tt>proc.HandleableProcessor</tt> 以指定要运行的标注处理程序。 <br> <img alt=""Annotation Processor FQN"(标注处理程序 FQN)对话框的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-processor-fqn.png" title=""Annotation Processor FQN"(标注处理程序 FQN)对话框">
<p>"Properties"(属性)窗口中的 "Compiling"(编译)类别应如下图所示。</p>
<img alt="项目 "Properties"(属性)窗口中的 "Compiling"(编译)类别的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-properties-compiling.png" title="项目 "Properties"(项目属性)窗口中的 "Compiling"(编译)类别">
</li>
<li>在 "Properties"(属性)窗口中单击 "OK"(确定)。
<p class="notes"><strong>注:</strong><tt>Main.java</tt> 文件中,仍可能会看到一些编译错误。这是由于 IDE 仍然找不到声明 <tt>handleStuff</tt> 方法的 <tt>MainExtras.java</tt> 文件。首次构建 Demo 项目后,将构建 <tt>MainExtras.java</tt> 文件。如果为项目启用了 "Compile On Save"(在保存时编译)功能,则在保存 <tt>Main.java</tt> 时,IDE 将编译项目。</p>
</li>
<li>右键单击 "Demo" 项目,然后选择 "Build"(构建)。
<p>在构建项目后,如果在 "Projects"(项目)窗口中查看该项目,则可以看到包含 <tt>demo/MainExtras.java</tt> 文件的新 <tt>Generated Sources</tt>(构建的源文件)节点。</p>
<img alt="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-generated-sources.png" title="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口">
<p>如果您查看生成的 <tt>MainExtras.java</tt> 文件内容,则可以看到标注处理程序生成了包含 <tt>handleStuff</tt> 方法的 <tt>MainExtras</tt> 类。<tt>handleStuff</tt> 方法是通过标注的 <tt>Main.java</tt> 文件调用的。 </p>
<pre class="examplecode">package demo;
public abstract class MainExtras {
protected MainExtras() {}
/** Handle something. */
protected final void handleStuff(java.lang.String value) {
System.out.println(value);
}
}</pre>
</li>
<li>右键单击 "Demo" 项目,然后选择 "Run"(运行)。
<p>单击 "Run"(运行)时,"Output"(输出)窗口中应显示以下内容。Demo 项目编译并打印该信息。</p>
<img alt="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口的屏幕快照" class="margin-around b-all" src="../../../images_www/articles/72/java/annotations/demo-run.png" title="带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口">
<!-- <img src="../../../images_www/articles/69/java/annotations/output-demo.png" alt="Output of the Demo project." width="408" height="123" class="margin-around"></li>-->
</ol>
<div class="feedback-box"><a href="/about/contact_form.html?to=3&subject=Feedback:%20Using%20the%20Annotation%20Processors%20Support%20in%20NetBeans%20IDE">请将您的反馈意见发送给我们</a><br style="clear:both;" />
</div>
<h2><a id="seealso" name="seealso"></a>另请参见</h2>
<p>有关 Java 应用程序中标注的详细信息,请参见以下资源:</p>
<ul>
<li>Java SE 文档 - <a href="http://download.oracle.com/javase/6/docs/technotes/guides/language/annotations.html" target="_blank">标注</a></li>
<li>Java SE 教程 - <a href="http://download.oracle.com/javase/tutorial/java/javaOO/annotations.html" target="_blank">标注</a> </li>
<li><a href="http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#processing" target="_blank">Java 编译器:标注处理选项</a> </li>
<li><a href="http://blogs.sun.com/darcy/">Joseph D. Darcy 的博客</a> - 来自 JSR-269 规范负责人的有用提示 </li>
</ul>
</body>
</html>