JavaAnnotation入门

摘要:

本文针对java初学者或者annotation初次使用者全面地说明了annotation的使 用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程 序,但是对于一些高级的annotation应用(例如使用自定义annotation生成 javabean映射xml文件)还需要进一步的研究和探讨。涉及到深入annotation的内 容,作者将在后文《Java Annotation高级应用》中谈到。

同时,annotation运行存在两种方式:运行时、编译时。上文中讨论的都是在 运行时的annotation应用,但在编译时的annotation应用还没有涉及,

一、为什么使用Annotation:

在JAVA应用中,我们常遇到一些需要使用模版代码。例如,为了编写一个JAX -RPC web service,我们必须提供一对接口和实现作为模版代码。如果使用 annotation对远程访问的方法代码进行修饰的话,这个模版就能够使用工具自动 生成。

另 外,一些API需要使用与程序代码同时维护的附属文件。例如,JavaBeans 需要一个BeanInfo Class与一个Bean同时使用/维护,而EJB则同样需要一个部署 描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利而 且减少了错误。

二、Annotation工作方式:

在5.0 版之前的Java平台已经具有了一些ad hoc annotation机制。比如,使 用transient修饰符来标识一个成员变量在序列化子系统中应被忽略。而 @deprecated这个 javadoc tag也是一个ad hoc annotation用来说明一个方法已 过时。从Java5.0版发布以来,5.0平台提供了一个正式的annotation功能:允许 开发者定义、使用自己的annoatation类型。此功能由一个定义annotation类型的 语法和一个描述annotation声明的语法,读取annotaion 的API,一个使用 annotation修饰的class文件,一个annotation处理工具(apt)组成。

annotation并不直接影响代码语义,但是它能够工作的方式被看作类似程序的 工具或者类库,它会反过来对正在运行的程序语义有所影响。annotation可以从 源文件、class文件或者以在运行时反射的多种方式被读取。

当然annotation在某种程度上使javadoc tag更加完整。一般情况下,如果这 个标记对java文档产生影响或者用于生成java文档的话,它应该作为一个javadoc tag;否则将作为一个annotation。

三、Annotation使用方法:

1。类型声明方式:

通常,应用程序并不是必须定义annotation类型,但是定义annotation类型并 非难事。Annotation类型声明于一般的接口声明极为类似,区别只在于它在 interface关键字前面使用“@”符号。

annotation 类型的每个方法声明定义了一个annotation类型成员,但方法声 明不必有参数或者异常声明;方法返回值的类型被限制在以下的范围: primitives、String、Class、enums、annotation和前面类型的数组;方法可以 有默认值。

下面是一个简单的annotation类型声明:

清单1:

/**   * 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类型RequestForEnhancement。

2。修饰方法的annotation声明方式:

annotation 是一种修饰符,能够如其它修饰符(如public、static、final) 一般使用。习惯用法是annotaions用在其它的修饰符前面。 annotations由 “@+annotation类型+带有括号的成员-值列表”组成。这些成员的值必须是编译 时常量(即在运行时不变)。

A:下面是一个使用了RequestForEnhancement annotation的方法声明:

清单2:

@RequestForEnhancement(     id    = 2868724,     synopsis = "Enable time-travel",     engineer = "Mr. Peabody",     date   = "4/1/3007"   )   public static void travelThroughTime(Date destination) { ... }

B:当声明一个没有成员的annotation类型声明时,可使用以下方式:

清单3:

/**   * Indicates that the specification of the annotated API element   * is preliminary and subject to change.   */   public @interface Preliminary { }

作为上面没有成员的annotation类型声明的简写方式:

清单4:

@Preliminary public class TimeTravel { … }

C:如果在annotations中只有唯一一个成员,则该成员应命名为value:

清单5:

/**   * Associates a copyright notice with the annotated API element.   */   public @interface Copyright {     String value();   }

更为方便的是对于具有唯一成员且成员名为value的annotation(如上文), 在其使用时可以忽略掉成员名和赋值号(=):

清单6:

@Copyright(“2002 Yoyodyne Propulsion Systems”)

public class OscillationOverthruster { … }

3。一个使用实例:

结合上面所讲的,我们在这里建立一个简单的基于annotation测试框架。首先 我们需要一个annotation类型来表示某个方法是一个应该被测试工具运行的测试 方法。

清单7:

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

值得注意的是annotaion类型声明是可以标注自己的,这样的annotation被称 为“meta-annotations”。

在 上面的代码中,@Retention(RetentionPolicy.RUNTIME)这个meta- annotation表示了此类型的 annotation将被虚拟机保留使其能够在运行时通过反 射被读取。而@Target(ElementType.METHOD)表示此类型的 annotation只能用于 修饰方法声明。

下面是一个简单的程序,其中部分方法被上面的annotation所标注:

清单8:

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() { }   }Here is the testing tool:   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);    }   }

这个程序从命令行参数中取出类名,并且遍历此类的所有方法,尝试调用其中 被上面的测试annotation类型标注过的方法。在此过程中为了找出哪些方法被 annotation类型标注过,需要使用反射的方式执行此查询。如果在调用方法时抛 出异常,此方法被认为已经失败,并打印一个失败报告。最后,打印运行通过/失 败的方法数量。

下面文字表示了如何运行这个基于annotation的测试工具:

清单9:

$ 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

四、Annotation分类:

根据annotation的使用方法和用途主要分为以下几类:

1。内建Annotation——Java5.0版在java语法中经常用到的内建Annotation:

@Deprecated用于修饰已经过时的方法;

@Override用于修饰此方法覆盖了父类的方法(而非重载);

@SuppressWarnings用于通知java编译器禁止特定的编译警告。

下面代码展示了内建Annotation类型的用法:

清单10:

package com.bjinfotech.practice.annotation;/*** 演示如何使用java5内建的annotation* 参考资料:* http://java.sun.com/docs/books/tuTorial/java/javaOO/annotations.html* http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html* http://mindprod.com/jgloss/annotations.html* @author cleverpig**/import java.util.List;public class UsingBuiltInAnnotation {     //食物类     class Food{}//干草类     class Hay extends Food{}     //动物类     class Animal{         Food getFood(){             return null;         }         //使用Annotation声明Deprecated方法         @Deprecated         void deprecatedMethod(){         }     }     //马类-继承动物类     class Horse extends Animal{         //使用Annotation声明覆盖方法         @Override         Hay getFood(){             return new Hay();         }         //使用Annotation声明禁止警告         @SuppressWarnings({"deprecation","unchecked"})         void callDeprecatedMethod(List horseGroup){             Animal an=new Animal();             an.deprecatedMethod();             horseGroup.add(an);         }     }}

2。开发者自定义Annotation:由开发者自定义Annotation类型。

下面是一个使用annotation进行方法测试的sample:

AnnotationDefineForTestFunction类型定义如下:

清单11:

package com.bjinfotech.practice.annotation;import java.lang.annotation.*;/*** 定义annotation* @author cleverpig**///加载在VM中,在运行时进行映射@Retention(RetentionPolicy.RUNTIME)//限定此annotation只能标示方法@Target(ElementType.METHOD)public @interface AnnotationDefineForTestFunction{}

测试annotation的代码如下:

清单12:

package com.bjinfotech.practice.annotation;import java.lang.reflect.*;/*** 一个实例程序应用前面定义的Annotation: AnnotationDefineForTestFunction* @author cleverpig**/public class UsingAnnotation {     @AnnotationDefineForTestFunction public static void method01(){}     public static void method02(){}     @AnnotationDefineForTestFunction public static void method03(){         throw new RuntimeException("method03");     }     public static void method04(){         throw new RuntimeException("method04");     }     public static void main(String[] argv) throws Exception{         int passed = 0, failed = 0;         //被检测的类名         String className="com.bjinfotech.practice.annotation.UsingAnnotation";         //逐个检查此类的方法,当其方法使用annotation声明时调 用此方法       for (Method m : Class.forName(className).getMethods()) {        if (m.isAnnotationPresent (AnnotationDefineForTestFunction.class)) {          try {           m.invoke(null);           passed++;          } catch (Throwable ex) {           System.out.printf("测试 %s 失败: %s %n", m, ex.getCause());           failed++;          }        }       }       System.out.printf("测试结果: 通过: %d, 失败: %d%n", passed, failed);     }}

3。使用第三方开发的Annotation类型

这也是开发人员所常常用到的一种方式。比如我们在使用Hibernate3.0时就可 以利用Annotation生成数据表映射配置文件,而不必使用Xdoclet。

五、总结:

1。前面的文字说明了annotation的使用方法、定义方式、分类。初学者可以 通过以上的说明制作简单的annotation程序,但是对于一些高级的 annotation应 用(例如使用自定义annotation生成javabean映射xml文件)还需要进一步的研究 和探讨。

2。同时,annotation运行存在两种方式:运行时、编译时。上文中讨论的都 是在运行时的annotation应用,但在编译时的annotation应用还没有涉及,因为 编译时的annotation要使用annotation processing tool。

涉及以上2方面的深入内容,作者将在后文《Java Annotation高级应用》中谈 到。

懂得接受失败的人,就是懂得人生真谛的人,

JavaAnnotation入门

相关文章:

你感兴趣的文章:

标签云: