Java中Annotation(註釋)系列學習筆記(4)

【轉】Java中Annotation(註釋)系列學習筆記(4)

(四)使用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>

 



 

總結
   通過上面生成的xml文件,我們可以看出,通過使用APT工具確實可以簡化程序開發,程序員只需把一些關鍵信息通過Annotation寫在程序中,然後使用APT工具就可生在額外的文件。

 

轉自:http://blog.sina.com.cn/heyitang

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章