Java Annotations
原文:http://java.sun.com/javase/6/docs/technotes/guides/language/annotations.html
许多 APIs 需要模板代码. 例如, 为了编写 JAX-RPC web 服务, 你必须提供一对接口和实现. 这种模板代码其实可以由工具自动生成, 如果程序使用了注释进行修饰以指示哪些方法可远程访问.
其它 APIs 需要有与程序一起的额外附属文件. 例如 JavaBeans 需要一个与 bean 一同存在的 BeanInfo 类, 而 Enterprise JavaBeans (EJB) 需要一个 部署描述符. 如果这些文件中的信息随同程序在一块会更加方便, 也会不易出错.
The Java platform 已经有各种特定的 annotation 机制. 例如 transient 修饰符是一个特定 annotation 用于指示一个域应该在序列化时忽略, @deprecated javadoc tag 用于指示该方法不应该再使用了. 到了 Java 5.0, 平台具有常规用途的 annotation (也称之为 metadata) 设施, 允许你定义和使用自己的 annotation types. 该设施提供的语法可声明 annotation types, 对声明进行注释, 读取 annotations 的 APIs, 表示 annotations 的 class 文件, 和一个 annotation processing tool.
Annotations 不会直接影响程序语义, 但它们却影响到工具和类库如何处理程序的方式, 进而反过来影响到正在运行程序的语义. 可从源文件, class 文件, 或者在运行期进行反射, 读取到 Annotations.
Annotations 完善了 javadoc tags. 通常, 如果标记是打算用于影响或生成 documentation, 那么它就应该是一个 javadoc tag; 否则, 它就应该是一个 annotation.
大部分程序员永远都不会自己去定义一个 annotation type, 但也不难做到. Annotation type 声明类似于通常的 interface 声明. 一个 (@) 位于 interface 关键字之前. 每个方法声明定义一个 element. 方法声明不能有任何参数或 throws 子句. 返回类型限制在 原始类型, String, Class, enums, annotations, 和上述类型的数组. 方法可以有 默认值. 这是一个 annotation type 声明的例子:
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
*/
public @interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date() default "[unimplemented]";
}
annotation type 定义后, 你可以用它注释声明. 注释是一种特殊类型的修饰符, 可以在其它修饰符(例如 public, static, or final)所能用的任何地方进行使用. 按惯例, 注释位于其它修饰符之前. 注释由一个 (@) 跟着一个 annotation type 和一个圆括号所包围的值对列表组成. 值必须是编译期常量. 这是用上面声明的 annotation type 进行修饰的方法声明:
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
annotation type 不带有任何元素, 则称之为 marker annotation type, 例如:
/**
* Indicates that the specification of the annotated API element
* is preliminary and subject to change.
*/
public @interface Preliminary { }
marker annotation 允许省略圆括号, 如下:
@Preliminary public class TimeTravel { ... }
在只有一个元素的注释中, 元素应该命名为 value, 如下:
/**
* Associates a copyright notice with the annotated API element.
*/
public @interface Copyright {
String value();
}
在单一元素且元素名为 value 的注释里, 允许省略元素名字和等号(=), 如下:
@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }
为了把它们捏合到一块, 我们创建了一个简单的基于注释的测试框架. 首先我们需要一个 marker annotation type 用于指示一个方法是要测试的方法, 应该由测试工具运行:
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }
注意, annotation type 声明自己也被注释过. 这种注释称为 meta-annotations. 第一个(@Retention(RetentionPolicy.RUNTIME)) 指明这种类型的注释由 VM 持有, 这样就可以在运行期反射读取到它们. 第二个 (@Target(ElementType.METHOD)) 指明这个注释类型只可用于注释方法声明.
这是一个例子, 其中一些方法使用上面注释进行了注释:
public class Foo {
@Test public static void m1() { }
public static void m2() { }
@Test public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test public static void m5() { }
public static void m6() { }
@Test public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() { }
}
这是测试工具:
import java.lang.reflect.*;
public class RunTests {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName(args[0]).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
该工具把命令行参数作为类名, 然后迭代该 class 的所有方法, 尝试调用每个由 Test 注释过的方法. 通过反射查找方法是否有一个 Test 注释由绿色加亮显示. 如果测试方法调用抛出异常, 则认为该测试已经失败, 接着打印出失败报告. 最终, 打印一份测试通过和失败数的汇总. 如下是运行测试工具测试 Foo 程序的结果:
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2
虽然这个测试工具完全是玩具级的, 但它演示了注释的威力, 可以很容易的进行扩展进行增强.
Categorized in: Java · Tagged with: Annotation, Java


(