Java学习笔记(53)------------Annotation(注释)第二篇

JDK的元Annotation

 

JDK除了java.lang下提供了3个基本Annotation之外,还在java.lang.annotation包下提供了四个Meta Annotation(元Annotation),这四个都是用于修饰其它Annotation定义

 

(1)使用@Retention


  @Retention只能用于修饰一个Annotation定义,用于该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值.

 


value成员变量的值只能是如下三个

RetentionPolicy.CLASS:编译器将把注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值


RentionPolicy.RUNTIME:编译器将把注释记录在class文件中。当运行java程序时,JVM也会保留注释,程序也可以通过反射获取该注释。


RentionPolicy.SOURCE:编译器直接丢弃这种策略的注释。

 

  在前面的程序中,因为我们要通过反射获取注释信息,所以我们指定value属性值为RetentionPolicy.RUNTIME. 使用@Retention元数据Annotation可采用如下代码为value指定值.


//定义下面的Testable Annotation的保留到过行时
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Testable{}

 

也可以采用如下代码来为value指定值
@Retention(RetentionPolicy.SOURCE)
public @interface Testable{}

  

 

上面代码使用@Retention元数据Annotation时,并未直接通过value=RetentionPolicy.SOURCE的方式来为成员变量指定值,这是因为如果Annotation的成员变量名为value时,程序中可以直接在Annotation后的括号里指定该成员变量的值,无须用name=value的形式.

说明
  如果我们定义的Annotation类型里只有一个value成员变量,使用该Annotation时可以直接在Annotation后的括号里指定value成员变量的值,无须使用name=value的形式。

 

(2)使用@Target


  @Target也是用于修饰一个Annotation定义,它用于指定被修饰的Annotation能用于修饰哪些程序元素。@Target Annotation也包含一个名为value的成员变量。该成员变量的值只能是如下几个

 

ElementType.ANNOTATION_TYPE: 指定该策略的Annotation只能修饰Annotation


ElementType.CONSTRUCTOR:指定该策略的Annotation能修饰构造器


ElementType.FIELD:                 指定该策略的Annotation只能修饰成员变量


ElementType.LOCAL_VARIABLE:指定该策略的Annotation只能修饰局部变量


ElementType.METHOD             指定该策略的Annotation只能修饰方法定义


ElementType.PACKAGE           指定该策略的Annotaion只能修饰包定义


ElementType.PARAMETER      指定该策略的Annotation可以修饰参数


ElementType.TYPE                   指定该策略的Annotaion可以修饰类,接口(包括注释类型)或枚举定义

  与使用@Retention类似的是,使用@Target也可以直接在括号里指定value值,可以无须使用name=value的形式。如

 

下代码指定@ActionListenerFor Annotation只能修饰成员变量


@Target(ElementType.FIELD)
public @interface ActionListenerFor{}

 

如下代码指定@Testable Annotation只能修饰方法


@Target(ElementType.METHOD)
public @interface Testable{}

(3)使用@Documented
  @Documented用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotatin类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素API文档中将会包含该Annotation说明

  

 

下面程序定义了一个Testable Annotation程序使用@Documented来修饰@Testable Annotation定义,所以该Annotation将被 javadoc工具提取

[java]  view plain copy
  1. <span style="font-size:18px;color:#333333;">@Retention(RetentionPolicy.RUNTIME)     
  2. @Target(ElementType.METHOD)  
  3. //定义Testable Annotation将被javadoc工具提取  
  4. @Documented  
  5. public @interface Testable  
  6. {  
  7. }</span>  

 

上面程序中的@Documented代码决定了所有使用@Testable Annotation的地方都会被javadoc工具提取到api文档中

 

  下面程序中定义了一个 MyTest类,该类中的infor方法使用Testable Annotation修饰
程序清单

[java]  view plain copy
  1. <span style="font-size:18px;">public class MyTest  
  2. {  
  3.  //使用@Testable修饰info方法  
  4.  @Testable  
  5.  public void info()  
  6.  {  
  7.   System.out.println("info方法...");  
  8.  }  
  9. }</span>  


 


(4)使用@Inherited
  @Inherited元Annotation指定被它修饰的Annotation将具有继承性。如果某个类使用了Annotaion(使用Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释。

 

下面使用@Inherited元数据注释定义了一个Inherited Annotation,该Annotation将具有继承性

程序清单

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">@Target(ElementType.TYPE)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. // @Inherited  
  4. public @interface Inheritable  
  5. {  
  6. }</span>  


 

  上面程序中表明了@Inheritable Annotation具有继承性,如果某个类使用了该Annotation 修饰,则该类的子类将自动具有@Inheritable Annotation

  下面程序定义了一个Base基类,该基类使用了@Inherited修饰,则Base类的子类将自动具有@Inherited Annotation

程序清单

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">//使用@Inheritable修饰的Base类  
  2. @Inheritable  
  3. class Base  
  4. {  
  5. }  
  6. //TestInheritable类只是继承了Base类,  
  7. //并未直接使用@Inheritable Annotiation修饰  
  8. public class TestInheritable extends Base  
  9. {  
  10.  <wbr>public static void main(String[] args)  
  11.  <wbr>{  
  12.  <wbr> <wbr>//打印TestInheritable类是否具有Inheritable Annotation  
  13.  <wbr> <wbr>System.out.println(TestInheritable.class.isAnnotationPresent(Inheritable.class));  
  14.  <wbr>}  
  15. }</wbr></wbr></wbr></wbr></wbr></wbr></wbr></span>  


 

总结
   1.
上面程序中的Base类使用了@Inheritable Annotation修饰,而该Annotaion具有可继承性,所以其子类也将具有@Inheritable Annotation,运行上面程序看到输出 true
   2.如果将上面的Inheritable.java程序中的@Inherited注释或者删除,将会导致Inheritable Annotation不具有继承性,运行上面程序将会输出 false

 

 

使用APT处理Annotation

 

APT(Annotation processing tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
  Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件.
  使用APT主要的目的是简化开发者的工作量,因为APT可以编译程序源代码的同时,生成一些附属文件(比如源文件,类文件,程序发布描述文件等),这些附属文件的内容也都是与源代码相关的,换句话说,使用APT可以代替传统的对代码信息和附属文件的维护工作。
  如果有过Hibernate开发经验的朋友可能知道每写一个Java文件,还必须额外地维护一个Hibernate映射文件(一个名为*.hbm.xml的文件,当然可以有一些工具可以自动生成),下面将使用Annotation来简化这步操作。

  为了使用系统的apt工具来读取源文件中的Annotation,程序员必须自定义一个Annotation处理器,编写Annotation处理器需要使用JDK lib目录中的tools.jar 里的如下4个包.

com.sun.mirror.apt:和APT交互的接口
com.sun.mirror.declaration:包含各种封装类成员,类方法,类声明的接口。
com.sun.mirror.type:包含各种封装源代码中程序元素的接口。
com.sun.mirror.util:提供了用于处理类型和声明的一些工具。

  每个Annotation处理器需要实现com.sun.mirror.apt包下的AnnotationProcessor接口,这个接口中定义了一个"process"方法,该方法是由apt调用Annotation处理器时将被用到的。

一个Annotation处理器可以处理一种或多种Annotation类型。

1.通常情况下,Annotation处理器实例是由其相应的工厂返回,Annotation处理器工厂应该实现AnnotationProcessorFactory接口,APT将调用工厂类的getProcessorFor方法来获得Annotation处理器。
2.在调用过程中,APT将提供给工厂类一个AnnotationProcessorEnvironment对象.
3.AnnotationProcessorEnvironment对象是APT工具与注释环境通信的途径。

  使用APT工具来处理源文件时,APT首先检测在源代码文件中包含哪些Annotation,然后APT将查找所需的处理器工厂,并由工厂来返回相应的Annotation处理器。如果该处理器工厂支持这些Annotaion,处理器工厂返回的Annotaion处理器将会处理这些Annotation,如果生成的源文件中再次包含Annotaion,APT将会重复上面过程,直至没有新文件生成。

  为了说明使用APT来根据源文件中的注释来生成额外的文件,下面将定义三个Annotation类型,分别用于修饰持久化类,标识属性和普通属性。

程序清单

 

修饰表属性

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Persistent(table="persons_table")  
  2. public class Person  
  3. {  
  4.  @IdProperty(column="person_id",type="integer",generator="identity")  
  5.  private int id;  
  6.  @Property(column="person_name",type="string")  
  7.  private String name;  
  8.  @Property(column="person_age",type="integer")  
  9.  private int age;  
  10.   
  11.  public Person()  
  12.  {  
  13.  }  
  14.   
  15.  public Person(int id , String name , int age)  
  16.  {  
  17.   this.id = id;  
  18.   this.name = name;  
  19.   this.age = age;  
  20.  }  
  21.   
  22.  public void setId(int id)  
  23.  {  
  24.   this.id = id;  
  25.  }  
  26.  public int getId()  
  27.  {  
  28.    return this.id;  
  29.  }  
  30.  public void setName(String name)  
  31.  {  
  32.   this.name = name;  
  33.  }  
  34.  public String getName()  
  35.  {  
  36.    return this.name;  
  37.  }  
  38.   
  39.  public void setAge(int age)  
  40.  {  
  41.   this.age = age;  
  42.  }  
  43.  public int getAge()  
  44.  {  
  45.    return this.age;  
  46.  }  
  47.   
  48. }  


定义了三个Annotation之后,下面我们提供一个简单的Java类文件,这个Java类文件使用了上面三个Annotation来修饰

 

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Persistent(table="persons_table")  
  2. public class Person  
  3. {  
  4.  @IdProperty(column="person_id",type="integer",generator="identity")  
  5.  private int id;  
  6.  @Property(column="person_name",type="string")  
  7.  private String name;  
  8.  @Property(column="person_age",type="integer")  
  9.  private int age;  
  10.   
  11.  public Person()  
  12.  {  
  13.  }  
  14.   
  15.  public Person(int id , String name , int age)  
  16.  {  
  17.   this.id = id;  
  18.   this.name = name;  
  19.   this.age = age;  
  20.  }  
  21.   
  22.  public void setId(int id)  
  23.  {  
  24.   this.id = id;  
  25.  }  
  26.  public int getId()  
  27.  {  
  28.    return this.id;  
  29.  }  
  30.  public void setName(String name)  
  31.  {  
  32.   this.name = name;  
  33.  }  
  34.  public String getName()  
  35.  {  
  36.    return this.name;  
  37.  }  
  38.   
  39.  public void setAge(int age)  
  40.  {  
  41.   this.age = age;  
  42.  }  
  43.  public int getAge()  
  44.  {  
  45.    return this.age;  
  46.  }  
  47.   
  48. }  


 

 


  上面Person类是一个非常普通的Java类,但这个普通的Java类使用了@Persistent,@IdProperty,@IdPropery三个Annotation。下面我们为这三个Annotation提供了一个Annotation处理器,该处理器的功能是根据注释来生成一个Hibernate的映射文件.

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 程序清单  
  2.   
  3. import com.sun.mirror.apt.*;  
  4. import com.sun.mirror.declaration.*;  
  5. import com.sun.mirror.type.*;  
  6. import com.sun.mirror.util.*;  
  7.   
  8. import java.beans.*;  
  9. import java.io.*;  
  10. import java.util.*;  
  11.   
  12. import java.lang.reflect.*;  
  13. public class HibernateAnnotationProcessor implements AnnotationProcessor  
  14. {  
  15.  //Annotation处理器环境,是该处理器与APT交互的重要途径  
  16.  private AnnotationProcessorEnvironment env;  
  17.  //构造HibernateAnnotationProcessor对象时,获得处理器环境  
  18.  public HibernateAnnotationProcessor(AnnotationProcessorEnvironment env)  
  19.  {  
  20.   this.env = env;  
  21.  }  
  22.  //循环处理每个对象  
  23.  public void process()  
  24.  {  
  25.   //遍历每个class文件  
  26.   for (TypeDeclaration t : env.getSpecifiedTypeDeclarations())  
  27.   {  
  28.    //定义一个文件输出流,用于生成额外的文件  
  29.    FileOutputStream fos = null;  
  30.    //获取正在处理的类名  
  31.    String clazzName = t.getSimpleName();  
  32.    //获取类定义前的Persistent Annotation  
  33.    Persistent per = t.getAnnotation(Persistent.class);  
  34.    //当per Annotation不为空时才继续处理  
  35.    if(per != null)  
  36.    {  
  37.     try  
  38.     {  
  39.      //创建文件输出流  
  40.      fos = new FileOutputStream(clazzName + ".hbm.xml");  
  41.      PrintStream ps = new PrintStream(fos);  
  42.      //执行输出  
  43.      ps.println("<?xml version="1.0"?>");  
  44.      ps.println("<!DOCTYPE hibernate-mapping");  
  45.      ps.println(" PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"");  
  46.      ps.println("    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">");  
  47.      ps.println("<hibernate-mapping>");  
  48.      ps.print(" <class name="" + t);  
  49.      //输出per的table()的值  
  50.      ps.println("" table="" + per.table() + "">");  
  51.      for (FieldDeclaration f : t.getFields())  
  52.      {  
  53.       //获取指定FieldDeclaration前面的IdProperty Annotation  
  54.       IdProperty id = f.getAnnotation(IdProperty.class);  
  55.       //如果id Annotation不为空  
  56.       if (id != null)  
  57.       {  
  58.        //执行输出  
  59.        ps.println("  <id name=""  
  60.         + f.getSimpleName()  
  61.         + "" column="" + id.column()  
  62.         + "" type="" + id.type()  
  63.         + "">");  
  64.        ps.println("   <generator class=""  
  65.         + id.generator() + ""/>");  
  66.        ps.println("  </id>");  
  67.       }  
  68.       //获取指定FieldDeclaration前面的Property Annotation  
  69.       Property p = f.getAnnotation(Property.class);  
  70.       //如果p Annotation不为空  
  71.       if (p != null)  
  72.       {  
  73.        //执行输出  
  74.        ps.println("  <property name=""  
  75.         + f.getSimpleName()  
  76.         + "" column="" + p.column()  
  77.         + "" type="" + p.type()  
  78.         + ""/>");    
  79.       }  
  80.      }  
  81.      ps.println(" </class>");  
  82.      ps.println("</hibernate-mapping>");  
  83.     }  
  84.     catch (Exception e)  
  85.     {  
  86.      e.printStackTrace();  
  87.     }  
  88.     finally  
  89.     {  
  90.      //关闭输出流  
  91.      try  
  92.      {  
  93.       if (fos != null)  
  94.       {  
  95.        fos.close();  
  96.       }  
  97.      }  
  98.      catch (IOException ex)  
  99.      {  
  100.       ex.printStackTrace();  
  101.      }  
  102.     }  
  103.    }  
  104.   }  
  105.  }  
  106. }  


 

 


  上面的Annotation处理器比较简单,与前面通过反射来获取Annotation信息不同的是,这个Annotation处理器使用AnnotationProcessorEnvironment来获取Annotation信息,AnnotationProcessorEnvironment包含了一个getSpecifiedTypeDeclarations方法,可获取所有需要处理的类声明,这个类声明可包括类,接口,和枚举等声明,由TypeDeclaration对象表地示,与Classc对象的功能大致相似,区别只是TypeDeclaration是静态,只要有类文件就可以获得该对象,而Class是动态的,必须由虚拟机装载了指定类文件后才会产生。

TypeDeclaration又包含了如下三个常用方法来获得对应的程序元素。

getFields:获取该类声明里的所有成员变量声明,返回值是集合元素FieldDeclaration的集合
getMethods:获取该类声明里的所有成员声明,返回值是集合元素MethodDeclaration的集合
getPackage:获取该类声明里的包声明,返回值是TypeDeclaration

  上面三个方法返回的TypeDeclaration,FieldDeclaration,MethodDeclaration都可调用getAnnotation方法来访问修饰它们的Annotation,上面程序中就是获取不同程序元素的Annotation的代码。

  提供了上面的Annotation处理器类之后,还应该为该Annotation处理器提供一个处理工厂,处理工厂负责决定该处理器支持哪些Annotation,并通过getProcessorFor方法来生成一个Annotation处理哭对象。

程序清单如下

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import com.sun.mirror.apt.*;  
  2. import com.sun.mirror.declaration.*;  
  3. import com.sun.mirror.type.*;  
  4. import com.sun.mirror.util.*;  
  5.   
  6. import java.beans.*;  
  7. import java.io.*;  
  8. import java.util.*;  
  9. public class HibernateAnnotationFactory implements AnnotationProcessorFactory  
  10. {  
  11.  //所有支持的注释类型  
  12.  public Collection<String> supportedAnnotationTypes()  
  13.  {  
  14.   return Arrays.asList("Property" , "IdProperty" , "Persistent");  
  15.  }  
  16.  //返回所有支持的选项  
  17.  public Collection<String> supportedOptions()  
  18.  {  
  19.   return Arrays.asList(new String[0]);  
  20.  }  
  21.  //返回Annotation处理器  
  22.  public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds,AnnotationProcessorEnvironment env)  
  23.  {  
  24.   return new HibernateAnnotationProcessor(env);  
  25.  }    
  26. }  


 

 


  提供了上面的处理器工厂后,就可以使用API工具来处理上面的Person.java源文件,并根据该源文件来生成一个XML文件。 APT工具位于JDK的安装路径的bin路径下。。


java Annotation的讲解就这些了,需要好好看一看呀!

 

博客借鉴http://blog.sina.com.cn/s/blog_4c925dca0100hsyt.html

http://blog.csdn.net/zhai56565/article/details/40503743


良心的公众号,更多精品文章,不要忘记关注哈

《Android和Java技术栈》


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值