Java中Annotation(注释)系列学习笔记(2)

来源:互联网 发布:facebook代理软件 编辑:程序博客网 时间:2024/06/12 18:35

二.自定义Annotation
  前面已经介绍了如何使用java.lang包下的三个标准Annotation。下面介绍如何定义 Annotation,并利用Annotation完成一些实际的功能。

(1)定义Annotation
  定义新的Annotation类型时使用@interface关健字(在原有的interface关健字前增加@符号),它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像。

如下代码定义一个简单的Annotation
//定义一个简单的Annotation类型
public @interface Test
{
}
  定义该Annotation之后,就可以在程序任何地方使用该Annotation ,使用Annotation时的语法非常类似于public ,final这样的修饰符,通常可以用于修饰程序中的类,方法,变量,接口等定义。
  通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其它成员变量指定值,因而Annotation的长度可能较长,所以通常把Annotaion另放一行.

//使用@Test修饰类定义
@Test
public class MyClass
{
...
}

默认情况下,Annotation可用于修饰任何元素,包括类,接口,方法等。如下程序使用@TestAnnotation来修饰方法
public class MyClass
{
   //使用@TestAnnotation修饰方法
   @Test
   public void info()
   {
 ...
   }
}

  Annotation不仅可以是这种简单的Annotation,Annotation还可以带用员变量,Annotation的成员变量在Annotation定义中以无参数来声明。其方法和返回值定义了该成员的名字和类型。
如下代码可以定义一个有成员变量的Annotation
public @interface MyTag
{
    //定义两个成员变量Annotation
    //Annotation中的成员变量以方法的形式来定义
    String name();
    int age();
}

  仔细观察:上面定义Annotation的代码与定义接口的语法非常像,只是上面MyTag使用@interface关健字来定义,而接口使用interface来定义

  一旦在Annotation里定义了成员变量之后,使用该Annotation时应该为该Annotation的成员变量指定值,如下代码所示
public class Test
{
    //使用带成员变量的Annotation时,需要为成员变量付值
    MyTag(name='heyitang',age=30)
    public void info()
    {
 ...
    }
}

  我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关健字。如下代码定义了MyTag Annotaion,该Annotation里包含了两个成员:name和age,这两个成元变量使用default指定了默认值
public @interface MyTag
{
   //定义了两个成员变量的Annotaion
   //以default为两个成员变量指定初始值
   String name() default "yeeku";
   int age() default 32;
}
 
  如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值,如下代码所示
public class Test
{
    //使用带成员变量的Annotation
    //因为它的成员变量有默认值,所以可以无须为成员变量指定值
    @MyTag
    public void info()
    {
        ...
    }
}

    当然我们介绍的Annotation是否可以包含成员指定值,如果MyTag为的成员变量指定了值,则默认值不会起作用
    总结
       根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类
       1.标记Annotation:一个没有成员变量的Annotation类型被称为标记。这种Annotation仅合使用自身的存在与否来为我们提供信息。如前面介绍的@Override,@Test等Annotation
       2.元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation


(2)提取Annotation
  前面已经提到:JAVA使用Annotation接口来代表元素前面的注释,该接品是所有Annotation类型的父接口。除此之外,JAVA在java.lang.reflect包下新增了AnnotationElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类.

    Class:类定义
    Constructor:构造器定义
    Field:类的成员变量定义
    Method:类的方法定义
    Package:类的包定义

    AnnotationElement接口是所有程序元素(如Class,Method,Constructor)的父接口,所以程序通过反射获取某个类的了AnnotationElement对象(如Class,Method,和Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息


    getAnnotation(Class<T> annotationClass):返回该程序元素上存在的,指定类型的注释
    Annotation[] getAnnotations:返回该程序元素上存在的所有注释
    boolean isAnnotationPreset(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false

 

(3)使用Annotation的例子
  下面介绍两个使用Annotation的例子,第一个Annotation Testable没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的.
Testable.java源文件

import java.lang.annotation.*;
 
@Retention(RetentionPolicy.RUNTIME)   
@Target(ElementType.METHOD)
//定义Testable Annotation将被javadoc工具提取
@Documented
public @interface Testable
{
}

  上面程序定义了一个标记Testable Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指明Testabel注释可以保留多久,而@Target注释指定Testable能修饰的目标(只能是方法)。

  如下MyTest测试用例里定义了8个方法,这8个方法没有太大的区别,其中四个方法使用@testable注释来标记这些方法是可测试的
public class MyTest
{
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m1()
 {
 }
 public static void m2()
 {
  
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m3()
       
  throw new RuntimeException("Boom"); 
 }
 public static void m4()
 {
      
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m5()
 {
 
    public static void m6()
 {
 }
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m7()
           
  throw new RuntimeException("Crash");  
       
 public static void m8()
 {
 }
}


  正如前面提到的,仅仅使用注释来标记程序元素对程序是不会有任何影响的,这也是注释的一条重要原则,为了让程序中这些注释起作用,我们必须为这些注释提供一个注释处理工具。
  下面的注释处理工具分析目标类,如果目标类中方法使用了@Testable注释修饰,则通过反射来运行该测试方法

import java.lang.reflect.*;
public class TestProcessor
{
 public static void process(String clazz)
  throws ClassNotFoundException
 {
  int passed = 0;
  int failed = 0;
  //遍历obj对象的所有方法
  for (Method m : Class.forName(clazz).getMethods())
  {
   //如果包含@Testable标记注释
   if (m.isAnnotationPresent(Testable.class))
   {
    try
    {
     //调用m方法
     m.invoke(null);
     //passed加1
     passed++;
    }
    catch (Exception ex)
    {
     System.out.printf("方法" + m + "运行失败,异常:" + ex.getCause() + "\n");
     failed++;
    }
   }
  }
  //统计测试结果
  System.out.printf("共运行了:" + (passed + failed)+ "个方法,其中:\n" +
   "失败了:" + failed + "个,\n" + 
   "成功了:" + passed + "个!\n");
 }
}

  该TestProcessor类里只包含了一个process方法,该方法可接受一个字符串参数,该方法将会分析clazz参数所代表的类,并运行该类里的,使用@Testabel注释修饰的方法

 

该程序的主类比较简单,只提供主方法
import java.lang.reflect.*;
public class RunTests
{
 public static void main(String[] args) throws Exception
 {
  //处理MyTest类
  TestProcessor.process("MyTest");
 }
}

 

运行最终效果图如下

Java中Annotation(注释)系列学习笔记(2)


总结
    通过这个运行结果可以看出,程序中的@Testable Annotation起作用了,MyTest类里以@Testable注释修饰的方法被正常测试


http://blog.sina.com.cn/s/blog_4c925dca0100hseu.html

0 0
原创粉丝点击