Java5.0的元注解
许多API都需要为数不少的“模板式”的代码,比如,如果想写一个JAX-RPC(JAX-RPC即Java API for XML-Based RPC-译注)的web service,你需要提供一对接口及其实现程序。如果程序本身能被表明哪些方法可以由远程访问的注释所“修饰”的话,那么这种“模板式”的代码就可以用工具自动生成。
另一些API需要在维护程序的同时维护一些“辅助”文件,比如,维护JavaBean的同时需要维护BeanInfo类,Enterprise JavaBeans (EJB) 则需要部署说明档(deployment descriptor)。如果这些辅助信息能在程序中作为注解来维护,就会方便地多,并且也不容易犯错。
Java平台已经提供了几种不同的注解机制。比如transient修饰符就是一种特别的注解,标明被修饰的字段在序列化的子系统应该被忽略,@deprecated 作为javadoc的特殊标签,标明该方法不应该再用。在5.0版本中,Java平台提供了一种一般性的注解(即元数据metadata)机制,允许你自定义注解类型和使用该类型。该机制包含注解的定义语法、声明语法、读取注解的API、注解的代理类文件以及注解的一个处理工具。
注解并不直接对程序语义产生影响,但是会对工具或库在处理程序的方式上时产生影响,进而影响程序在运行时的语义。注解可以从源文件或类文件中读取,甚至在运行时以反射方式读取。
注解是对javadoc标签的补充。一般而言,如果标注的目的是影响或产生文档,那么就该用javadoc标签,否则就用注解。典型的应用程序的程序员永远不必自己定义一个注解类型,但其实真要做也不难。注解类型的定义跟接口定义相似,只是需要在inteface关键字前面加一个at符(@),声明一个方法即为注解类型定义一个元素。方法声明时不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、枚举enums、注解类型,或前面这些的数组,方法可以有默认值,下面是一个定义注解类型的例子:
/**
* 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]";
}一旦完成定义,就可以使用它做注解声明。注解是一个特殊的修饰符,可以和其他修饰符一样(如public,static,final)用在各种地方。为方便计,注解一般放在最前面。注解修饰符由@符号、注解类型名和括号括起来的元素名/值对组成。下面这个例子声明一个方法使用了上面所定义的注解。
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
注解类型如果不含任何元素,被称为“标记”注解类型,如:
/**
* Indicates that the specification of the annotated API element
* is preliminary and subject to change.
*/
public @interface Preliminary { }
“标记”注解类型允许忽略括号,如下:
@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 { ... }
我们将建一个简单的基于注解的测试框架,将前面所讲的合起来用。首先我们定义一个“标记”注解类型,来说明某个方法是一个测试方法,需要被测试工具执行:
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 { }
注意这个注解本身也是被注解的。这些注解被称为“元注解” (meta-annotations). 第一个(@Retention(RetentionPolicy.RUNTIME)) 表示注解由虚拟机保留,可以在运行时通过反射读取;第二个(@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);
}
}
这个工具程序将一个类名作为命令行运行时的参数,然后遍历该类的所有方法,并尝试调用每一个被“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
这个测试工具无疑是个玩具程序,但它表明了注解的强大,并能很容易的扩展为一个有用的工具。
- 默认分类(20)
- J2EE(25)
- Java(56)
- PHP(55)
- SEO(10)
- 网页设计(20)
- 网站建设(37)
- 数据库(7)
- JavaScript(17)
- JQuery(6)
- MySQL(20)
- SQL Server(6)
- Access(1)
- Oracle(6)
- office(6)
- Dreamweaver(4)
- Photoshop(12)
- Flash(9)
- Fireworks(13)
- CSS(14)
- HTML(4)
- .NET(7)
- ASP(2)
- DB2(1)
- Ajax(2)
- Linux(12)
- Struts(7)
- Hibernate(8)
- Spring(2)
- Jsp(22)
- Asp(8)
- C#(3)
- C++(1)
- 网络安全(5)
- 软件工程(7)
- XML(1)
- English(2)
- 计算机等级考试(2)
- 计算机病毒(4)
- 个人日志(76)
- 互联网(15)
- ActionScript(10)
- Android(3)
- 数据结构与算法(1)
- 游戏策略(3)
- 美文翻译(2)
- 编程开发(19)
- 计算机应用(4)
- 计算机(10)
- Unity3d(6)
- 其他(1)
- egret(1)