注解(Annotation)是Java5中的新特性,一直看到很多项目在使用,但是自己一直不是很了解,现在有机会学习了一下,记录下来,今后用得着。
1.注解可以做什么?
注解将一些系统所需的数据信息(不方便用Java语言来表达),加入到了Java源代码中,而不需要额外的信息提供者。比如配置信息、一些样板文件(接口文件)等。可以通过在代码中添加注解,来直接配置你的系统。
著名的Java单元测试框架Junit-4就是大量使用了注解的功能,加入到了其单元测试中,方面了用户使用JUnit进行测试,用户只需要在想要测试的方法上添加@Test 这样的标签就可以将该方法变为一个单元测试用例。
下面,我将仿照JUnit的测试框架来简单介绍一下注解功能(例子来自于java.sun.com)
2.注解怎么使用
我把注解的使用分为三个步骤:
第一,定义注解
第二,给源程序添加注解
第三,解析注解(实际工作在这里完成)
首先看定义注解
注解的定义看起来和定义接口差不多,就是在interface关键字前多一个@标签
/** * Describes the Request-For-Enhancement(RFE) that led * to the presence of the annotated API element. */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) public @interface RequestForEnhancement { int id(); String synopsis(); String engineer() default "[unassigned]"; String date(); default "[unimplemented]"; }
其中的id()、synopsis()可以认为是该注解的元素,或者属性,这些方法声明不能有任何参数,或者抛出异常,可以通过default 来定义它的默认值。而且这些类型只能是原语类型、String、Annotation或者这些类型的数组。
最上面的@Target表面了改注解使用的范围(是一个方法还是一个类,或者一个域),@Retention表面了注解可以用在哪个级别上(源代码中、类文件中、还是运行时)
这样,便定义了一个注解
接下来给源程序添加注解:
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
这样就使用刚才定义的RequestForEnhancement注解,描述了一个方法,可以看见在()中,定义了给该注解的各个元素的参数,就相当于初始化了该注解
最后解析注解,这里我们使用一个简单的测试框架来表述
定义一个Test注解,使用在运行时,定义在方法上,它没有任何方法,只是一个标注
在解析程序中,获取该标注,并采取相应动作。在这里,使用了Test标签标注的方法,都将被测试,并形成测试结果。
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 { }
添加将被测试的方法:
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() { }
}
在这里用@Test标签标注了m1 m3 m5 m7方法,这些方法将在运行测试用例时被测试。
在该类中运行了测试用例,它使用反射机制,调用了被标注为@Test标签的方法,如果抛出异常则认为测试没有通过,反之则通过。
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);
}
}
在这里,可以讲上面的这些类、注解可以和JUnit中的进行一个对比
@Test就是JUnit框架中的@Test
被标注的方法将被测试
RunTest类就相当于手动启动JUnit进行单元测试时运行的org.junit.runner.JUnitCore.runClasses(不是使用IDE启动测试用例,如果使用Eclipse类似的IDE,你可能只是点击一个Run As JUnit)
这样,就通过注解完成了一个简单的测试框架
详细信息可以查看
http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html