Java Annotation 高级应用

来源:互联网 发布:iphone吉他软件 编辑:程序博客网 时间:2024/05/16 04:38
前言: 
前不久在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 高级应用》
0 0