Java Annotation 高級應用

前言: 
前不久在matrix上先後發表了《java annotation 入門》 、《java annotation 手冊》 兩 篇文章,比較全面的對java annotation的語法、原理、使用三方面進行了闡述。由於《入門》中的簡單例程雖然簡單明瞭的說明了annotation用法,但給大家的感覺可能 是意猶未見,所以在此行文《java annotation 高級應用》,具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。 

一、摘要: 
《java annotation 高級應用》具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。本文列舉了用於演示annotation的BRFW演示框架、演示APT的apt代碼實例,並對其進行 較爲深度的分析,希望大家多多提意見。 

二、annotation實例分析 
1.BRFW(Beaninfo Runtime FrameWork)定義: 
本人編寫的一個annotation功能演示框架。顧名思義,BRFW就是在運行時取得bean信息的框架。 

2.BRFW的功能: 
A.源代碼級annotation :在bean的源代碼中使用annotation定義bean的信息; 
B.運行時獲取bean數據 :在運行時分析bean class中的annotation,並將當前bean class中field信息取出,功能類似xdoclet; 
C.運行時bean數據的xml綁定 :將獲得的bean數據構造爲xml文件格式展現。熟悉j2ee的朋友知道,這個功能類似jaxb。 

3.BRFW框架: 
BRFW主要包含以下幾個類: 
A.Persistent類 :定義了用於修飾類的固有類型成員變量的annotation。 
B.Exportable類 :定義了用於修飾Class的類型的annotation。 
C.ExportToXml類 :核心類,用於完成BRFW的主要功能:將具有Exportable Annotation的bean對象轉換爲xml格式文本。 
D.AddressForTest類 :被A和B修飾過的用於測試目的的地址bean類。其中包含了地址定義所必需的信息:國家、省級、城市、街道、門牌等。 
E.AddressListForTest類 : 被A和B修飾過的友人通訊錄bean類。其中包含了通訊錄所必備的信息:友人姓名、年齡、電話、住址(成員爲AddressForTest類型的 ArrayList)、備註。需要說明的是電話這個bean成員變量是由字符串類型組成的ArrayList類型。由於朋友的住址可能不唯一,故這裏的住 址爲由AddressForTest類型組成的ArrayList。 
從上面的列表中,可以發現A、B用於修飾bean類和其類成員;C主要用於取出bean類的數據並將其作xml綁定,代碼中使用了E作爲測試類;E中可能包含着多個D。 
在瞭解了這個簡單框架後,我們來看一下BRFW的代碼吧! 

4.BRFW源代碼分析: 
A.Persistent類: 
清單1: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.runtimeframework;  
  2.   
  3.   
  4.   
  5. import java.lang.annotation.*;  
  6.   
  7.   
  8.   
  9. /** 
  10.  
  11.  * 用於修飾類的固有類型成員變量的annotation 
  12.  
  13.  * @author cleverpig 
  14.  
  15.  * 
  16.  
  17.  */  
  18.   
  19. @Retention(RetentionPolicy.RUNTIME)  
  20.   
  21. @Target(ElementType.FIELD)  
  22.   
  23. public @interface Persistent {   
  24.   
  25.         String value() default "";  
  26.   
  27. }  



B.Exportable類: 
清單2: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.runtimeframework;  
  2.   
  3.   
  4.   
  5. import java.lang.annotation.*;  
  6.   
  7.   
  8.   
  9. /** 
  10.  
  11.  * 用於修飾類的類型的annotation 
  12.  
  13.  * @author cleverpig 
  14.  
  15.  * 
  16.  
  17.  */  
  18.   
  19. @Retention(RetentionPolicy.RUNTIME)  
  20.   
  21. @Target(ElementType.TYPE)  
  22.   
  23. public @interface Exportable {  
  24.   
  25.         //名稱  
  26.   
  27.         String name() default "";  
  28.   
  29.         //描述  
  30.   
  31.         String description() default "";  
  32.   
  33.         //省略name和description後,用來保存name值  
  34.   
  35.         String value() default "";  
  36.   
  37.           
  38.   
  39. }  


C.AddressForTest類: 
清單3: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.runtimeframework;  
  2.   
  3.   
  4.   
  5. /** 
  6.  
  7.  * 用於測試的地址類 
  8.  
  9.  * @author cleverpig 
  10.  
  11.  * 
  12.  
  13.  */  
  14.   
  15. @Exportable("address")  
  16.   
  17. public class AddressForTest {  
  18.   
  19.         //國家  
  20.   
  21.         @Persistent  
  22.   
  23.         private String country=null;  
  24.   
  25.           
  26.   
  27.         //省級  
  28.   
  29.         @Persistent  
  30.   
  31.         private String province=null;  
  32.   
  33.           
  34.   
  35.         //城市  
  36.   
  37.         @Persistent  
  38.   
  39.         private String city=null;  
  40.   
  41.           
  42.   
  43.         //街道  
  44.   
  45.         @Persistent  
  46.   
  47.         private String street=null;  
  48.   
  49.   
  50.   
  51.         //門牌  
  52.   
  53.         @Persistent  
  54.   
  55.         private String doorplate=null;  
  56.   
  57.           
  58.   
  59.         public AddressForTest(String country,String province,  
  60.   
  61.                         String city,String street,String doorplate){  
  62.   
  63.                 this.country=country;  
  64.   
  65.                 this.province=province;  
  66.   
  67.                 this.city=city;  
  68.   
  69.                 this.street=street;  
  70.   
  71.                 this.doorplate=doorplate;  
  72.   
  73.         }  
  74.   
  75.           
  76.   
  77. }  


D.AddressListForTest類: 
清單4: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.runtimeframework;  
  2.   
  3.   
  4.   
  5. import java.util.*;  
  6.   
  7.   
  8.   
  9. /** 
  10.  
  11.  * 友人通訊錄 
  12.  
  13.  * 包含:姓名、年齡、電話、住址(多個)、備註 
  14.  
  15.  * @author cleverpig 
  16.  
  17.  * 
  18.  
  19.  */  
  20.   
  21. @Exportable(name="addresslist",description="address list")  
  22.   
  23. public class AddressListForTest {  
  24.   
  25.         //友人姓名  
  26.   
  27.         @Persistent  
  28.   
  29.         private String friendName=null;  
  30.   
  31.           
  32.   
  33.         //友人年齡  
  34.   
  35.         @Persistent  
  36.   
  37.         private int age=0;  
  38.   
  39.           
  40.   
  41.         //友人電話  
  42.   
  43.         @Persistent  
  44.   
  45.         private ArrayList<String> telephone=null;  
  46.   
  47.           
  48.   
  49.         //友人住址:家庭、單位  
  50.   
  51.         @Persistent  
  52.   
  53.         private ArrayList<AddressForTest> AddressForText=null;  
  54.   
  55.           
  56.   
  57.         //備註  
  58.   
  59.         @Persistent  
  60.   
  61.         private String note=null;  
  62.   
  63.           
  64.   
  65.         public AddressListForTest(String name,int age,  
  66.   
  67.                         ArrayList<String> telephoneList,   
  68.   
  69.                         ArrayList<AddressForTest> addressList,  
  70.   
  71.                         String note){  
  72.   
  73.                 this.friendName=name;  
  74.   
  75.                 this.age=age;  
  76.   
  77.                 this.telephone=new ArrayList<String>(telephoneList);  
  78.   
  79.                 this.AddressForText=new ArrayList<AddressForTest>(addressList);  
  80.   
  81.                 this.note=note;  
  82.   
  83.                   
  84.   
  85.         }  
  86.   
  87. }  



E.ExportToXml類: 
清單5: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.runtimeframework;  
  2.   
  3.   
  4.   
  5. import java.lang.reflect.Field;  
  6.   
  7. import java.util.Collection;  
  8.   
  9. import java.util.Iterator;  
  10.   
  11. import java.util.Map;  
  12.   
  13. import java.util.ArrayList;  
  14.   
  15.   
  16.   
  17. /** 
  18.  
  19.  * 將具有Exportable Annotation的對象轉換爲xml格式文本  
  20.  
  21.  * @author cleverpig 
  22.  
  23.  * 
  24.  
  25.  */  
  26.   
  27. public class ExportToXml {  
  28.   
  29.         /** 
  30.  
  31.          * 返回對象的成員變量的值(字符串類型) 
  32.  
  33.          * @param field 對象的成員變量 
  34.  
  35.          * @param fieldTypeClass 對象的類型 
  36.  
  37.          * @param obj 對象 
  38.  
  39.          * @return 對象的成員變量的值(字符串類型) 
  40.  
  41.          */  
  42.   
  43.         private String getFieldValue(Field field,Class fieldTypeClass,Object obj){  
  44.   
  45.                 String value=null;  
  46.   
  47.                   
  48.   
  49.                 try{  
  50.   
  51.                         if (fieldTypeClass==String.class){  
  52.   
  53.                                 value=(String)field.get(obj);  
  54.   
  55.                         }  
  56.   
  57.                         else if (fieldTypeClass==int.class){  
  58.   
  59.                                 value=Integer.toString(field.getInt(obj));  
  60.   
  61.                         }  
  62.   
  63.                         else if (fieldTypeClass==long.class){  
  64.   
  65.                                 value=Long.toString(field.getLong(obj));  
  66.   
  67.                         }  
  68.   
  69.                         else if (fieldTypeClass==short.class){  
  70.   
  71.                                 value=Short.toString(field.getShort(obj));  
  72.   
  73.                         }  
  74.   
  75.                         else if (fieldTypeClass==float.class){  
  76.   
  77.                                 value=Float.toString(field.getFloat(obj));  
  78.   
  79.                         }  
  80.   
  81.                         else if (fieldTypeClass==double.class){  
  82.   
  83.                                 value=Double.toString(field.getDouble(obj));  
  84.   
  85.                         }  
  86.   
  87.                         else if (fieldTypeClass==byte.class){  
  88.   
  89.                                 value=Byte.toString(field.getByte(obj));  
  90.   
  91.                         }  
  92.   
  93.                         else if (fieldTypeClass==char.class){  
  94.   
  95.                                 value=Character.toString(field.getChar(obj));  
  96.   
  97.                         }  
  98.   
  99.                         else if (fieldTypeClass==boolean.class){  
  100.   
  101.                                 value=Boolean.toString(field.getBoolean(obj));  
  102.   
  103.                         }  
  104.   
  105.                 }  
  106.   
  107.                 catch(Exception ex){  
  108.   
  109.                         ex.printStackTrace();  
  110.   
  111.                         value=null;  
  112.   
  113.                 }  
  114.   
  115.                 return value;  
  116.   
  117.         }  
  118.   
  119.           
  120.   
  121.         /** 
  122.  
  123.          * 輸出對象的字段,當對象的字段爲Collection或者Map類型時,要調用exportObject方法繼續處理 
  124.  
  125.          * @param obj 被處理的對象 
  126.  
  127.          * @throws Exception 
  128.  
  129.          */  
  130.   
  131.         public void exportFields(Object obj) throws Exception{  
  132.   
  133.                 Exportable exportable=obj.getClass().getAnnotation(Exportable.class);          
  134.   
  135.                 if (exportable!=null){  
  136.   
  137.                         if (exportable.value().length()>0){  
  138.   
  139. //                                System.out.println("Class annotation Name:"+exportable.value());  
  140.   
  141.                         }  
  142.   
  143.                         else{  
  144.   
  145. //                                System.out.println("Class annotation Name:"+exportable.name());  
  146.   
  147.                         }  
  148.   
  149.                 }  
  150.   
  151.                 else{  
  152.   
  153. //                        System.out.println(obj.getClass()+"類不是使用Exportable標註過的");  
  154.   
  155.                 }  
  156.   
  157.                   
  158.   
  159.                 //取出對象的成員變量  
  160.   
  161.                 Field[] fields=obj.getClass().getDeclaredFields();  
  162.   
  163.                   
  164.   
  165.                 for(Field field:fields){  
  166.   
  167.                         //獲得成員變量的標註  
  168.   
  169.                         Persistent fieldAnnotation=field.getAnnotation(Persistent.class);  
  170.   
  171.                         if (fieldAnnotation==null){  
  172.   
  173.                                 continue;  
  174.   
  175.                         }  
  176.   
  177.                         //重要:避免java虛擬機檢查對私有成員的訪問權限  
  178.   
  179.                         field.setAccessible(true);  
  180.   
  181.                         Class typeClass=field.getType();  
  182.   
  183.                         String name=field.getName();  
  184.   
  185.                         String value=getFieldValue(field,typeClass,obj);  
  186.   
  187.                           
  188.   
  189.                         //如果獲得成員變量的值,則輸出  
  190.   
  191.                         if (value!=null){  
  192.   
  193.                                 System.out.println(getIndent()+"<"+name+">\n"  
  194.   
  195.                                                 +getIndent()+"\t"+value+"\n"+getIndent()+"</"+name+">");  
  196.   
  197.                         }  
  198.   
  199.                         //處理成員變量中類型爲Collection或Map  
  200.   
  201.                         else if ((field.get(obj) instanceof Collection)||  
  202.   
  203.                                         (field.get(obj) instanceof Map)){  
  204.   
  205.                                 exportObject(field.get(obj));  
  206.   
  207.                         }  
  208.   
  209.                         else{  
  210.   
  211.                                 exportObject(field.get(obj));  
  212.   
  213.                         }  
  214.   
  215.                           
  216.   
  217.                 }  
  218.   
  219.         }  
  220.   
  221.           
  222.   
  223.         //縮進深度  
  224.   
  225.         int levelDepth=0;  
  226.   
  227.         //防止循環引用的檢查者,循環引用現象如:a包含b,而b又包含a  
  228.   
  229.         Collection<Object> cyclicChecker=new ArrayList<Object>();  
  230.   
  231.           
  232.   
  233.         /** 
  234.  
  235.          * 返回縮進字符串 
  236.  
  237.          * @return 
  238.  
  239.          */  
  240.   
  241.         private String getIndent(){  
  242.   
  243.                 String s="";  
  244.   
  245.                 for(int i=0;i<levelDepth;i++){  
  246.   
  247.                         s+="\t";  
  248.   
  249.                 }  
  250.   
  251.                 return s;  
  252.   
  253.         }  
  254.   
  255.         /** 
  256.  
  257.          * 輸出對象,如果對象類型爲Collection和Map類型,則需要遞歸調用exportObject進行處理 
  258.  
  259.          * @param obj 
  260.  
  261.          * @throws Exception 
  262.  
  263.          */  
  264.   
  265.         public void exportObject(Object obj) throws Exception{  
  266.   
  267.                 Exportable exportable=null;  
  268.   
  269.                 String elementName=null;  
  270.   
  271.                   
  272.   
  273.                 //循環引用現象處理  
  274.   
  275.                 if (cyclicChecker.contains(obj)){  
  276.   
  277.                         return;  
  278.   
  279.                 }  
  280.   
  281.                   
  282.   
  283.                 cyclicChecker.add(obj);  
  284.   
  285.                   
  286.   
  287.                 //首先處理Collection和Map類型  
  288.   
  289.                 if (obj instanceof Collection){  
  290.   
  291.                         for(Iterator i=((Collection)obj).iterator();i.hasNext();){  
  292.   
  293.                                 exportObject(i.next());  
  294.   
  295.                         }  
  296.   
  297.                 }  
  298.   
  299.                 else if (obj instanceof Map){  
  300.   
  301.                         for(Iterator i=((Map)obj).keySet().iterator();i.hasNext();){  
  302.   
  303.                                 exportObject(i.next());  
  304.   
  305.                         }  
  306.   
  307.                 }  
  308.   
  309.                 else{  
  310.   
  311.   
  312.   
  313.                         exportable=obj.getClass().getAnnotation(Exportable.class);  
  314.   
  315.                         //如果obj已經被Exportable Annotation修飾過了(注意annotation是具有繼承性的),  
  316.   
  317.                         //則使用其name作爲輸出xml的元素name  
  318.   
  319.                         if (exportable!=null){  
  320.   
  321.                                 if (exportable.value().length()>0){  
  322.   
  323.                                         elementName=exportable.value();  
  324.   
  325.                                 }  
  326.   
  327.                                 else{  
  328.   
  329.                                         elementName=exportable.name();  
  330.   
  331.                                 }  
  332.   
  333.                         }  
  334.   
  335.                         //未被修飾或者Exportable Annotation的值爲空字符串,  
  336.   
  337.                         //則使用類名作爲輸出xml的元素name  
  338.   
  339.                         if (exportable==null||elementName.length()==0){  
  340.   
  341.                                 elementName=obj.getClass().getSimpleName();  
  342.   
  343.                         }  
  344.   
  345.                         //輸出xml元素頭  
  346.   
  347.                         System.out.println(getIndent()+"<"+elementName+">");  
  348.   
  349.                         levelDepth++;  
  350.   
  351.                         //如果沒有被修飾,則直接輸出其toString()作爲元素值  
  352.   
  353.                         if (exportable==null){  
  354.   
  355.                                 System.out.println(getIndent()+obj.toString());  
  356.   
  357.                         }  
  358.   
  359.                         //否則將對象的成員變量導出爲xml  
  360.   
  361.                         else{  
  362.   
  363.                                 exportFields(obj);  
  364.   
  365.                         }  
  366.   
  367.                         levelDepth--;  
  368.   
  369.                         //輸出xml元素結尾  
  370.   
  371.                         System.out.println(getIndent()+"</"+elementName+">");  
  372.   
  373.                           
  374.   
  375.                 }  
  376.   
  377.                 cyclicChecker.remove(obj);  
  378.   
  379.         }  
  380.   
  381.           
  382.   
  383.         public static void main(String[] argv){  
  384.   
  385.                 try{  
  386.   
  387.                         AddressForTest ad=new AddressForTest("China","Beijing",  
  388.   
  389.                                         "Beijing","winnerStreet","10");  
  390.   
  391.                           
  392.   
  393.                         ExportToXml test=new ExportToXml();  
  394.   
  395.                           
  396.   
  397.                         ArrayList<String> telephoneList=new ArrayList<String>();  
  398.   
  399.                         telephoneList.add("66608888");  
  400.   
  401.                         telephoneList.add("66608889");  
  402.   
  403.                           
  404.   
  405.                         ArrayList<AddressForTest> adList=new ArrayList<AddressForTest>();  
  406.   
  407.                         adList.add(ad);  
  408.   
  409.                           
  410.   
  411.                         AddressListForTest adl=new AddressListForTest("coolBoy",  
  412.   
  413.                                         18,telephoneList,adList,"some words");  
  414.   
  415.                           
  416.   
  417.                         test.exportObject(adl);  
  418.   
  419.                 }  
  420.   
  421.                 catch(Exception ex){  
  422.   
  423.                         ex.printStackTrace();  
  424.   
  425.                 }  
  426.   
  427.         }  
  428.   
  429. }  


在ExportToXml類之前的類比較簡單,這裏必須說明一下ExportToXml類:此類的核心函數是exportObject和 exportFields方法,前者輸出對象的xml信息,後者輸出對象成員變量的信息。由於對象類型和成員類型的多樣性,所以採取了以下的邏輯: 

在exportObject方法中,當對象類型爲Collection和Map類型時,則需要遞歸調用exportObject進行處理; 
而如果對象類型不是Collection和Map類型的話,將判斷對象類是否被Exportable annotation修飾過: 
如果沒有被修飾,則直接輸出<對象類名>對象.toString()</對象類名>作爲xml綁定結果的一部分; 
如果被修飾過,則需要調用exportFields方法對對象的成員變量進行xml綁定。 

在 exportFields方法中,首先取出對象的所有成員,然後獲得被Persisitent annotation修飾的成員。在其後的一句:field.setAccessible(true)是很重要的,因爲bean類定義中的成員訪問修飾都 是private,所以爲了避免java虛擬機檢查對私有成員的訪問權限,加上這一句是必需的。接着後面的語句便是輸出<成員名>成員 值</成員名>這樣的xml結構。像在exportObject方法中一般,仍然需要判斷成員類型是否爲Collection和Map類型, 如果爲上述兩種類型之一,則要在exportFields中再次調用exportObject來處理這個成員。 

在main方法中,本人編寫了一段演示代碼:建立了一個由單個友人地址類(AddressForTest)組成的ArrayList作爲通訊錄類(AddressForTest)的成員的通訊錄對象,並且輸出這個對象的xml綁定,運行結果如下: 

清單6: 
Java代碼  收藏代碼
  1. <addresslist>  
  2.   
  3.         <friendName>  
  4.   
  5.                 coolBoy  
  6.   
  7.         </friendName>  
  8.   
  9.         <age>  
  10.   
  11.                 18  
  12.   
  13.         </age>  
  14.   
  15.         <String>  
  16.   
  17.                 66608888  
  18.   
  19.         </String>  
  20.   
  21.         <String>  
  22.   
  23.                 66608889  
  24.   
  25.         </String>  
  26.   
  27.         <address>  
  28.   
  29.                 <country>  
  30.   
  31.                         China  
  32.   
  33.                 </country>  
  34.   
  35.                 <province>  
  36.   
  37.                         Beijing  
  38.   
  39.                 </province>  
  40.   
  41.                 <city>  
  42.   
  43.                         Beijing  
  44.   
  45.                 </city>  
  46.   
  47.                 <street>  
  48.   
  49.                         winnerStreet  
  50.   
  51.                 </street>  
  52.   
  53.                 <doorplate>  
  54.   
  55.                         10  
  56.   
  57.                 </doorplate>  
  58.   
  59.         </address>  
  60.   
  61.         <note>  
  62.   
  63.                 some words  
  64.   
  65.         </note>  
  66.   
  67. </addresslist>  


三、APT實例分析: 
1.何謂APT? 
根 據sun官方的解釋,APT(annotation processing tool)是一個命令行工具,它對源代碼文件進行檢測找出其中的annotation後,使用annotation processors來處理annotation。而annotation processors使用了一套反射API並具備對JSR175規範的支持。 
annotation processors處理annotation的基本過程如下:首先,APT運行annotation processors根據提供的源文件中的annotation生成源代碼文件和其它的文件(文件具體內容由annotation processors的編寫者決定),接着APT將生成的源代碼文件和提供的源文件進行編譯生成類文件。 
簡單的和前面所講的annotation 實例BRFW相比,APT就像一個在編譯時處理annotation的javac。而且從sun開發者的blog中看到,java1.6 beta版中已將APT的功能寫入到了javac中,這樣只要執行帶有特定參數的javac就能達到APT的功能。 

2.爲何使用APT? 
使 用APT主要目的是簡化開發者的工作量,因爲APT可以在編譯程序源代碼的同時,生成一些附屬文件(比如源文件、類文件、程序發佈描述文字等),這些附屬 文件的內容也都是與源代碼相關的。換句話說,使用APT就是代替了傳統的對代碼信息和附屬文件的維護工作。使用過hibernate或者beehive等 軟件的朋友可能深有體會。APT可以在編譯生成代碼類的同時將相關的文件寫好,比如在使用beehive時,在代碼中使用annotation聲明瞭許多 struct要用到的配置信息,而在編譯後,這些信息會被APT以struct配置文件的方式存放。 

3.如何定義processor? 
A.APT工作過程: 
從 整個過程來講,首先APT檢測在源代碼文件中哪些annotation存在。然後APT將查找我們編寫的annotation processor factories類,並且要求factories類提供處理源文件中所涉及的annotation的annotation processor。接下來,一個合適的annotation processors將被執行,如果在processors生成源代碼文件時,該文件中含有annotation,則APT將重複上面的過程直到沒有新文 件生成。 

B.編寫annotation processors: 
編寫一個annotation processors需要使用java1.5 lib目錄中的tools.jar提供的以下4個包: 
com.sun.mirror.apt: 和APT交互的接口; 
com.sun.mirror.declaration: 用於模式化類成員、類方法、類聲明的接口; 
com.sun.mirror.type: 用於模式化源代碼中類型的接口; 
com.sun.mirror.util: 提供了用於處理類型和聲明的一些工具。 

每 個processor實現了在com.sun.mirror.apt包中的AnnotationProcessor接口,這個接口有一個名爲 “process”的方法,該方法是在APT調用processor時將被用到的。一個processor可以處理一種或者多種annotation類 型。 
一個processor實例被其相應的工廠返回,此工廠爲AnnotationProcessorFactory接口的實現。APT將調用工 廠類的getProcessorFor方法來獲得processor。在調用過程中,APT將提供給工廠類一個 AnnotationProcessorEnvironment 類型的processor環境類對象,在這個環境對象中,processor將找到其執行所需要的每件東西,包括對所操作的程序結構的參考,與APT通訊 併合作一同完成新文件的建立和警告/錯誤信息的傳輸。 

提供工廠類有兩個方式:通過APT的“-factory”命令行參數提供,或者讓工廠類在APT的發現過程中被自動定位(關於發現過程詳細介紹請看http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html )。前者對於一個已知的factory來講是一種主動而又簡單的方式;而後者則是需要在jar文件的META-INF/services目錄中提供一個特定的發現路徑: 
在 包含factory類的jar文件中作以下的操作:在META-INF/services目錄中建立一個名爲 com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8編碼文件,在文件中寫入所有要使用到的factory類全名,每個類爲一個單獨行。 

4.一個簡單的APT實例分析: 
A.實例構成: 
Review類:定義Review Annotation; 
ReviewProcessorFactory類:生成ReviewProcessor的工廠類; 
ReviewProcessor類:定義處理Review annotation的Processor; 
ReviewDeclarationVisitor類:定義Review annotation聲明訪問者,ReviewProcessor將要使用之對Class進行訪問。 
runapt.bat:定義了使用自定義的ReviewProcessor對Review類源代碼文件進行處理的APT命令行。 

B.Review類: 
清單7: 
Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.apt;  
  2.   
  3.   
  4.   
  5. /** 
  6.  
  7.  * 定義Review Annotation 
  8.  
  9.  * @author cleverpig 
  10.  
  11.  * 
  12.  
  13.  */  
  14.   
  15. public @interface Review {  
  16.   
  17.         public static enum TypeEnum{EXCELLENT,NICE,NORMAL,BAD};  
  18.   
  19.         TypeEnum type();  
  20.   
  21.         String name() default "Review";  
  22.   
  23. }  



C.ReviewProcessorFactory類: 
清單8: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.apt;  
  2.   
  3.   
  4.   
  5. import java.util.Collection;  
  6.   
  7. import java.util.Set;  
  8.   
  9. import java.util.Arrays;  
  10.   
  11. import com.sun.mirror.apt.*;  
  12.   
  13. import com.sun.mirror.declaration.AnnotationTypeDeclaration;  
  14.   
  15. import com.sun.mirror.apt.AnnotationProcessorEnvironment;  
  16.   
  17. //請注意爲了方便,使用了靜態import  
  18.   
  19. import static java.util.Collections.unmodifiableCollection;  
  20.   
  21. import static java.util.Collections.emptySet;  
  22.   
  23.   
  24.   
  25. /** 
  26.  
  27.  * 生成ReviewProcessor的工廠類 
  28.  
  29.  * @author cleverpig 
  30.  
  31.  * 
  32.  
  33.  */  
  34.   
  35. public class ReviewProcessorFactory implements AnnotationProcessorFactory{  
  36.   
  37.         /** 
  38.  
  39.          * 獲得針對某個(些)類型聲明定義的Processor 
  40.  
  41.          * @param atds 類型聲明集合 
  42.  
  43.          * @param env processor環境 
  44.  
  45.          */  
  46.   
  47.         public AnnotationProcessor getProcessorFor(  
  48.   
  49.                         Set<AnnotationTypeDeclaration> atds,   
  50.   
  51.                         AnnotationProcessorEnvironment env){  
  52.   
  53.                 return new ReviewProcessor(env);  
  54.   
  55.         }  
  56.   
  57.         /** 
  58.  
  59.          * 定義processor所支持的annotation類型 
  60.  
  61.          * @return processor所支持的annotation類型的集合 
  62.  
  63.          */  
  64.   
  65.         public Collection<String>         supportedAnnotationTypes(){  
  66.   
  67.                 //“*”表示支持所有的annotation類型  
  68.   
  69.                 //當然也可以修改爲“foo.bar.*”、“foo.bar.Baz”,來對所支持的類型進行修飾  
  70.   
  71.             return unmodifiableCollection(Arrays.asList("*"));  
  72.   
  73.     }  
  74.   
  75.           
  76.   
  77.         /** 
  78.  
  79.          * 定義processor支持的選項 
  80.  
  81.          * @return processor支持選項的集合 
  82.  
  83.          */  
  84.   
  85.         public Collection<String>         supportedOptions(){  
  86.   
  87.                 //返回空集合  
  88.   
  89.             return emptySet();  
  90.   
  91.     }  
  92.   
  93.           
  94.   
  95.         public static void main(String[] argv){  
  96.   
  97.                 System.out.println("ok");  
  98.   
  99.         }  
  100.   
  101. }  



D.ReviewProcessor類: 
清單9: 
Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.apt;  
  2.   
  3.   
  4.   
  5. import com.sun.mirror.apt.AnnotationProcessor;  
  6.   
  7. import com.sun.mirror.apt.AnnotationProcessorEnvironment;  
  8.   
  9. import com.sun.mirror.declaration.TypeDeclaration;  
  10.   
  11. import com.sun.mirror.util.DeclarationVisitors;  
  12.   
  13. import com.sun.mirror.util.DeclarationVisitor;  
  14.   
  15.   
  16.   
  17. /** 
  18.  
  19.  * 定義Review annotation的Processor 
  20.  
  21.  * @author cleverpig 
  22.  
  23.  * 
  24.  
  25.  */  
  26.   
  27. public class ReviewProcessor implements AnnotationProcessor{  
  28.   
  29.         //Processor所工作的環境  
  30.   
  31.         AnnotationProcessorEnvironment env=null;  
  32.   
  33.           
  34.   
  35.         /** 
  36.  
  37.          * 構造方法 
  38.  
  39.          * @param env 傳入processor環境 
  40.  
  41.          */  
  42.   
  43.         public ReviewProcessor(AnnotationProcessorEnvironment env){  
  44.   
  45.                 this.env=env;  
  46.   
  47.         }  
  48.   
  49.           
  50.   
  51.         /** 
  52.  
  53.          * 處理方法:查詢processor環境中的類型聲明, 
  54.  
  55.          */  
  56.   
  57.         public void process(){  
  58.   
  59.                 //查詢processor環境中的類型聲明  
  60.   
  61.                 for(TypeDeclaration type:env.getSpecifiedTypeDeclarations()){  
  62.   
  63.                         //返回對類進行掃描、訪問其聲明時使用的DeclarationVisitor,  
  64.   
  65.                         //傳入參數:new ReviewDeclarationVisitor(),爲掃描開始前進行的對類聲明的處理  
  66.   
  67.                         //        DeclarationVisitors.NO_OP,表示在掃描完成時進行的對類聲明不做任何處理  
  68.   
  69.                         DeclarationVisitor visitor=DeclarationVisitors.getDeclarationScanner(  
  70.   
  71.                                         new ReviewDeclarationVisitor(),DeclarationVisitors.NO_OP);  
  72.   
  73.                         //應用DeclarationVisitor到類型  
  74.   
  75.                         type.accept(visitor);  
  76.   
  77.                 }  
  78.   
  79.         }  
  80.   
  81. }  



E.ReviewDeclarationVisitor類: 
清單10: 

Java代碼  收藏代碼
  1. package com.bjinfotech.practice.annotation.apt;  
  2.   
  3.   
  4.   
  5. import com.sun.mirror.util.*;  
  6.   
  7. import com.sun.mirror.declaration.*;  
  8.   
  9.   
  10.   
  11. /** 
  12.  
  13.  * 定義Review annotation聲明訪問者 
  14.  
  15.  * @author cleverpig 
  16.  
  17.  * 
  18.  
  19.  */  
  20.   
  21. public class ReviewDeclarationVisitor extends SimpleDeclarationVisitor{  
  22.   
  23.         /** 
  24.  
  25.          * 定義訪問類聲明的方法:打印類聲明的全名 
  26.  
  27.          * @param cd 類聲明對象 
  28.  
  29.          */  
  30.   
  31.         public void visitClassDeclaration(ClassDeclaration cd){  
  32.   
  33.                 System.out.println("獲取Class聲明:"+cd.getQualifiedName());  
  34.   
  35.         }  
  36.   
  37.           
  38.   
  39.         public void visitAnnotationTypeDeclaration(AnnotationTypeDeclaration atd){  
  40.   
  41.                 System.out.println("獲取Annotation類型聲明:"+atd.getSimpleName());  
  42.   
  43.         }  
  44.   
  45.           
  46.   
  47.         public void visitAnnotationTypeElementDeclaration(AnnotationTypeElementDeclaration aed){  
  48.   
  49.                 System.out.println("獲取Annotation類型元素聲明:"+aed.getSimpleName());  
  50.   
  51.         }  
  52.   
  53. }  


F.runapt.bat文件內容如下: 
清單11: 
Java代碼  收藏代碼
  1. E:  
  2.   
  3. rem 項目根目錄  
  4.   
  5. set PROJECT_ROOT=E:\eclipse3.1RC3\workspace\tigerFeaturePractice  
  6.   
  7. rem 包目錄路徑  
  8.   
  9. set PACKAGEPATH=com\bjinfotech\practice\annotation\apt  
  10.   
  11. rem 運行根路徑  
  12.   
  13. set RUN_ROOT=%PROJECT_ROOT%\build  
  14.   
  15. rem 源文件所在目錄路徑  
  16.   
  17. set SRC_ROOT=%PROJECT_ROOT%\test  
  18.   
  19. rem 設置Classpath  
  20.   
  21. set CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%  
  22.   
  23.   
  24.   
  25. cd %SRC_ROOT%\%PACKAGEPATH%  
  26.   
  27. apt -nocompile -factory com.bjinfotech.practice.annotation.apt.ReviewProcessorFactory  ./*.java  



四、參考資源: 
http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html 
作者的Blog:http://blog.matrix.org.cn/page/cleverpig 
                         --摘自《Java Annotation 高級應用》
發佈了16 篇原創文章 · 獲贊 2 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章