加载中…
正文 字体大小:

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

(2010-04-18 00:04:52)
标签:

annotation

java

阿堂

网络时空

杂谈

分类: java技术

(四)使用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类型,分别用于修饰持久化类,标识属性和普通属性。

程序清单

 

修饰表属性
import java.lang.annotation.*;
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Persistent
{
 String table();
}

修饰标识属性
import java.lang.annotation.*;

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface IdProperty
{
 String column();
 String type();
 String generator();
}


修饰普通成员变量的Annotation
import java.lang.annotation.*;
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Property
{
 String column();
 String type();
}


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

@Persistent(table="persons_table")
public class Person
{
 @IdProperty(column="person_id",type="integer",generator="identity")
 private int id;
 @Property(column="person_name",type="string")
 private String name;
 @Property(column="person_age",type="integer")
 private int age;

 public Person()
 {
 }

 public Person(int id , String name , int age)
 {
  this.id = id;
  this.name = name;
  this.age = age;
 }

 public void setId(int id)
 {
  this.id = id;
 }
 public int getId()
 {
   return this.id;
 }
 public void setName(String name)
 {
  this.name = name;
 }
 public String getName()
 {
   return this.name;
 }

 public void setAge(int age)
 {
  this.age = age;
 }
 public int getAge()
 {
   return this.age;
 }

}


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

 

程序清单

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;

import java.beans.*;
import java.io.*;
import java.util.*;

import java.lang.reflect.*;
public class HibernateAnnotationProcessor implements AnnotationProcessor
{
 //Annotation处理器环境,是该处理器与APT交互的重要途径
 private AnnotationProcessorEnvironment env;
 //构造HibernateAnnotationProcessor对象时,获得处理器环境
 public HibernateAnnotationProcessor(AnnotationProcessorEnvironment env)
 {
  this.env = env;
 }
 //循环处理每个对象
 public void process()
 {
  //遍历每个class文件
  for (TypeDeclaration t : env.getSpecifiedTypeDeclarations())
  {
   //定义一个文件输出流,用于生成额外的文件
   FileOutputStream fos = null;
   //获取正在处理的类名
   String clazzName = t.getSimpleName();
   //获取类定义前的Persistent Annotation
   Persistent per = t.getAnnotation(Persistent.class);
   //当per Annotation不为空时才继续处理
   if(per != null)
   {
    try
    {
     //创建文件输出流
     fos = new FileOutputStream(clazzName + ".hbm.xml");
     PrintStream ps = new PrintStream(fos);
     //执行输出
     ps.println("<?xml version="1.0"?>");
     ps.println("<!DOCTYPE hibernate-mapping");
     ps.println(" PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"");
     ps.println("    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">");
     ps.println("<hibernate-mapping>");
     ps.print(" <class name="" + t);
     //输出per的table()的值
     ps.println("" table="" + per.table() + "">");
     for (FieldDeclaration f : t.getFields())
     {
      //获取指定FieldDeclaration前面的IdProperty Annotation
      IdProperty id = f.getAnnotation(IdProperty.class);
      //如果id Annotation不为空
      if (id != null)
      {
       //执行输出
       ps.println("  <id name=""
        + f.getSimpleName()
        + "" column="" + id.column()
        + "" type="" + id.type()
        + "">");
       ps.println("   <generator class=""
        + id.generator() + ""/>");
       ps.println("  </id>");
      }
      //获取指定FieldDeclaration前面的Property Annotation
      Property p = f.getAnnotation(Property.class);
      //如果p Annotation不为空
      if (p != null)
      {
       //执行输出
       ps.println("  <property name=""
        + f.getSimpleName()
        + "" column="" + p.column()
        + "" type="" + p.type()
        + ""/>");  
      }
     }
     ps.println(" </class>");
     ps.println("</hibernate-mapping>");
    }
    catch (Exception e)
    {
     e.printStackTrace();
    }
    finally
    {
     //关闭输出流
     try
     {
      if (fos != null)
      {
       fos.close();
      }
     }
     catch (IOException ex)
     {
      ex.printStackTrace();
     }
    }
   }
  }
 }
}


  上面的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处理哭对象。

程序清单如下

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;

import java.beans.*;
import java.io.*;
import java.util.*;
public class HibernateAnnotationFactory implements AnnotationProcessorFactory
{
 //所有支持的注释类型
 public Collection<String> supportedAnnotationTypes()
 {
  return Arrays.asList("Property" , "IdProperty" , "Persistent");
 }
 //返回所有支持的选项
 public Collection<String> supportedOptions()
 {
  return Arrays.asList(new String[0]);
 }
 //返回Annotation处理器
 public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds,AnnotationProcessorEnvironment env)
 {
  return new HibernateAnnotationProcessor(env);
  
}


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

运行APT命令时,可以使用-factory选项来指定处理器工厂类
如下所示
rem 使用HibernateAnnotationFactory作为处理器工厂来处理Person.java中的Annotation
apt -factory HibernateAnnotationFactory Person.java

  

  使用APT工具,HibernateAnnotationFactory工厂来处理Person.java后,将可以看到在相同路径下,生成了一个Person.hbm.xml文件了,该文件内容如下
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="Person" table="persons_table">
  <id name="id" column="person_id" type="integer">
   <generator class="identity"/>
  </id>
  <property name="name" column="person_name" type="string"/>
  <property name="age" column="person_age" type="integer"/>
 </class>
</hibernate-mapping>

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

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

 

总结
   通过上面生成的xml文件,我们可以看出,通过使用APT工具确实可以简化程序开发,程序员只需把一些关键信息通过Annotation写在程序中,然后使用APT工具就可生在额外的文件。

0

阅读 评论 收藏 转载 喜欢 打印举报
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有