随笔-6  评论-38  文章-40  trackbacks-0

JDK5.0中注释(Annotation)的用法

很多 API 都需要相当数量的样板代码,比如,为了编写一个 JAX-RPC WEB 服务,你需要提供一个接口和一个实现类。如果这个程序已经被加了注释 Annotations 以说明那个方法需要被远程调用,那么我们可以一个工具去自动生成这些样板代码。

还有一些 API 需要在程序代码另外维护一些文件,比如 JavaBean 需要一个 BeanInfo 类, EJB 需要一个部署描述文件。如果我们能够把这些需要另外维护的文件内容以注释 Annotation 的方式和代码放在一起维护,一定会更加方便同时也减少出错的机会。

Java 平台已经有了一些特别的注释的机制。比如 transient 修饰符就是一个特别的注释,表明这个字段应该被序列化子系统忽略; @deprecated javadoc 标签是一个特别的标签来说明某个方法已经不再被使用了。 JDK5.0 提供了一个让我们自己定义我们自己注释的功能,这个功能包含了如何定义注释类型的语法,声明注释的语法,读取注释的 API ,一个类文件保存注释(译者注:注释可以被看作一个类,我们需要用一个 .java 文件保存我们自己定义的注释源码)和一个注释处理的工具。

注释并不影响代码的语义,但却影响用于处理包含有注释的程序代码的工具的处理方式,使他们(工具)能够影响运行状态的程序的语义。注释可以从源代码中读取,从编译后的 .class 文件中读取,也可以通过反射机制在运行时读取。

注释是 JavaDoc 标签的补充。一般情况下,如果我们的主要目标是影响或者产生文档,那么我们应该使用 JavaDoc ;否则,我们应该使用注释 Annotations

一般的应用程序开发人员可能从不需要定义一个注释类型,但定义我们自己的注释类型并不复杂。注释类型的定义跟定义一个接口相似,我们需要在 interface 这个关键字前面加上一个 @ 符号。注释中的每一个方法定义了这个注释类型的一个元素,注释中方法的声明中一定不能包含参数,也不能抛出异常;方法的返回值被限制为简单类型、 String Class emnus 、注释,和这些类型的数组。方法可以有一个缺省值。这里是一个注释类型定义的例子:

/**
* 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 等)使用地方都可以使用。按照惯例,注释应该放在其他修饰符的前面。注释的声明用 @ 符号后面跟上这个注释类型的名字,再后面跟上括号,括号中列出这个注释中元素 / 方法的 key value 对。值必须是常量。这里是一个例子,使用上面定义的注释类型:

				
						
								@RequestForEnhancement(
						
				
				
						    id       = 2868724,
				
						    synopsis = 'Enable time-travel',
				
						    engineer = 'Mr. Peabody',
				
						    date     = '4/1/3007'
		
				)

				public static void travelThroughTime(Date destination) { ... }
				
				
		
				没有元素
				/
				方法的注释被成为标记(
				marker
				)注释类型,例如
				
						

				
				
				
						
								/**
						
				
		
				 * 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 { }

		

 

我们可以注意到这个注释类型本省也被注释了,这种注释叫做元注释。第一注释 (@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);

				
						   }

				}

		

 

这个工具用一个类名作为参数,遍历这个类中的所有方法,并调用其中被加了 @Test 注释的方法。如果一个方法抛出了一个异常,那么这个测试就失败了,最终的测试结果被打印了出来。下面是程序运行的结果:

 

				
						
								$ 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

		

 

 

虽然这个测试工具只是一个玩具,但他显示了注释的强大的功能。

 

posted on 2006-11-26 20:04 一手的小窝窝 阅读(384) 评论(0)  编辑  收藏 所属分类: JAVA

只有注册用户登录后才能发表评论。


网站导航: