《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:
package com.bjinfotech.practice.annotation.runtimeframework;import java.lang.annotation.*;/** * 用于修饰类的固有类型成员变量的annotation * @author cleverpig * */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Persistent { String value() default "";}package com.bjinfotech.practice.annotation.runtimeframework;import java.lang.annotation.*;/** * 用于修饰类的类型的annotation * @author cleverpig * */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Exportable { //名称 String name() default ""; //描述 String description() default ""; //省略name和description后,用来保存name值 String value() default ""; }package com.bjinfotech.practice.annotation.runtimeframework;/** * 用于测试的地址类 * @author cleverpig * */@Exportable("address")public class AddressForTest { //国家 @Persistent private String country=null; //省级 @Persistent private String province=null; //城市 @Persistent private String city=null; //街道 @Persistent private String street=null; //门牌 @Persistent private String doorplate=null; public AddressForTest(String country,String province, String city,String street,String doorplate){ this.country=country; this.province=province; this.city=city; this.street=street; this.doorplate=doorplate; } }package com.bjinfotech.practice.annotation.runtimeframework;import java.util.*;/** * 友人通讯录 * 包含:姓名、年龄、电话、住址(多个)、备注 * @author cleverpig * */@Exportable(name="addresslist",description="address list")public class AddressListForTest { //友人姓名 @Persistent private String friendName=null; //友人年龄 @Persistent private int age=0; //友人电话 @Persistent private ArrayList<String> telephone=null; //友人住址:家庭、单位 @Persistent private ArrayList<AddressForTest> AddressForText=null; //备注 @Persistent private String note=null; public AddressListForTest(String name,int age, ArrayList<String> telephoneList, ArrayList<AddressForTest> addressList, String note){ this.friendName=name; this.age=age; this.telephone=new ArrayList<String>(telephoneList); this.AddressForText=new ArrayList<AddressForTest>(addressList); this.note=note; }}package com.bjinfotech.practice.annotation.runtimeframework;import java.lang.reflect.Field;import java.util.Collection;import java.util.Iterator;import java.util.Map;import java.util.ArrayList;/** * 将具有Exportable Annotation的对象转换为xml格式文本 * @author cleverpig * */public class ExportToXml { /** * 返回对象的成员变量的值(字符串类型) * @param field 对象的成员变量 * @param fieldTypeClass 对象的类型 * @param obj 对象 * @return 对象的成员变量的值(字符串类型) */ private String getFieldValue(Field field,Class fieldTypeClass,Object obj){ String value=null; try{ if (fieldTypeClass==String.class){ value=(String)field.get(obj); } else if (fieldTypeClass==int.class){ value=Integer.toString(field.getInt(obj)); } else if (fieldTypeClass==long.class){ value=Long.toString(field.getLong(obj)); } else if (fieldTypeClass==short.class){ value=Short.toString(field.getShort(obj)); } else if (fieldTypeClass==float.class){ value=Float.toString(field.getFloat(obj)); } else if (fieldTypeClass==double.class){ value=Double.toString(field.getDouble(obj)); } else if (fieldTypeClass==byte.class){ value=Byte.toString(field.getByte(obj)); } else if (fieldTypeClass==char.class){ value=Character.toString(field.getChar(obj)); } else if (fieldTypeClass==boolean.class){ value=Boolean.toString(field.getBoolean(obj)); } } catch(Exception ex){ ex.printStackTrace(); value=null; } return value; } /** * 输出对象的字段,当对象的字段为Collection或者Map类型时,要调用exportObject方法继续处理 * @param obj 被处理的对象 * @throws Exception */ public void exportFields(Object obj) throws Exception{ Exportable exportable=obj.getClass().getAnnotation(Exportable.class); if (exportable!=null){ if (exportable.value().length()>0){// System.out.println("Class annotation Name:"+exportable.value()); } else{// System.out.println("Class annotation Name:"+exportable.name()); } } else{// System.out.println(obj.getClass()+"类不是使用Exportable标注过的"); } //取出对象的成员变量 Field[] fields=obj.getClass().getDeclaredFields(); for(Field field:fields){ //获得成员变量的标注 Persistent fieldAnnotation=field.getAnnotation(Persistent.class); if (fieldAnnotation==null){ continue; } //重要:避免java虚拟机检查对私有成员的访问权限 field.setAccessible(true); Class typeClass=field.getType(); String name=field.getName(); String value=getFieldValue(field,typeClass,obj); //如果获得成员变量的值,则输出 if (value!=null){ System.out.println(getIndent()+"<"+name+">\n" +getIndent()+"\t"+value+"\n"+getIndent()+"</"+name+">"); } //处理成员变量中类型为Collection或Map else if ((field.get(obj) instanceof Collection)|| (field.get(obj) instanceof Map)){ exportObject(field.get(obj)); } else{ exportObject(field.get(obj)); } } } //缩进深度 int levelDepth=0; //防止循环引用的检查者,循环引用现象如:a包含b,而b又包含a Collection<Object> cyclicChecker=new ArrayList<Object>(); /** * 返回缩进字符串 * @return */ private String getIndent(){ String s=""; for(int i=0;i<levelDepth;i++){ s+="\t"; } return s; } /** * 输出对象,如果对象类型为Collection和Map类型,则需要递归调用exportObject进行处理 * @param obj * @throws Exception */ public void exportObject(Object obj) throws Exception{ Exportable exportable=null; String elementName=null; //循环引用现象处理 if (cyclicChecker.contains(obj)){ return; } cyclicChecker.add(obj); //首先处理Collection和Map类型 if (obj instanceof Collection){ for(Iterator i=((Collection)obj).iterator();i.hasNext();){ exportObject(i.next()); } } else if (obj instanceof Map){ for(Iterator i=((Map)obj).keySet().iterator();i.hasNext();){ exportObject(i.next()); } } else{ exportable=obj.getClass().getAnnotation(Exportable.class); //如果obj已经被Exportable Annotation修饰过了(注意annotation是具有继承性的), //则使用其name作为输出xml的元素name if (exportable!=null){ if (exportable.value().length()>0){ elementName=exportable.value(); } else{ elementName=exportable.name(); } } //未被修饰或者Exportable Annotation的值为空字符串, //则使用类名作为输出xml的元素name if (exportable==null||elementName.length()==0){ elementName=obj.getClass().getSimpleName(); } //输出xml元素头 System.out.println(getIndent()+"<"+elementName+">"); levelDepth++; //如果没有被修饰,则直接输出其toString()作为元素值 if (exportable==null){ System.out.println(getIndent()+obj.toString()); } //否则将对象的成员变量导出为xml else{ exportFields(obj); } levelDepth--; //输出xml元素结尾 System.out.println(getIndent()+"</"+elementName+">"); } cyclicChecker.remove(obj); } public static void main(String[] argv){ try{ AddressForTest ad=new AddressForTest("China","Beijing", "Beijing","winnerStreet","10"); ExportToXml test=new ExportToXml(); ArrayList<String> telephoneList=new ArrayList<String>(); telephoneList.add("66608888"); telephoneList.add("66608889"); ArrayList<AddressForTest> adList=new ArrayList<AddressForTest>(); adList.add(ad); AddressListForTest adl=new AddressListForTest("coolBoy", 18,telephoneList,adList,"some words"); test.exportObject(adl); } catch(Exception ex){ ex.printStackTrace(); } }}<addresslist> <friendName> coolBoy </friendName> <age> 18 </age> <String> 66608888 </String> <String> 66608889 </String> <address> <country> China </country> <province> Beijing </province> <city> Beijing </city> <street> winnerStreet </street> <doorplate> 10 </doorplate> </address> <note> some words </note></addresslist>
package com.bjinfotech.practice.annotation.apt;/** * 定义Review Annotation * @author cleverpig * */public @interface Review { public static enum TypeEnum{EXCELLENT,NICE,NORMAL,BAD}; TypeEnum type(); String name() default "Review";}package com.bjinfotech.practice.annotation.apt;import java.util.Collection;import java.util.Set;import java.util.Arrays;import com.sun.mirror.apt.*;import com.sun.mirror.declaration.AnnotationTypeDeclaration;import com.sun.mirror.apt.AnnotationProcessorEnvironment;//请注意为了方便,使用了静态importimport static java.util.Collections.unmodifiableCollection;import static java.util.Collections.emptySet;/** * 生成ReviewProcessor的工厂类 * @author cleverpig * */public class ReviewProcessorFactory implements AnnotationProcessorFactory{ /** * 获得针对某个(些)类型声明定义的Processor * @param atds 类型声明集合 * @param env processor环境 */ public AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env){ return new ReviewProcessor(env); } /** * 定义processor所支持的annotation类型 * @return processor所支持的annotation类型的集合 */ public Collection<String> supportedAnnotationTypes(){ //“*”表示支持所有的annotation类型 //当然也可以修改为“foo.bar.*”、“foo.bar.Baz”,来对所支持的类型进行修饰 return unmodifiableCollection(Arrays.asList("*")); } /** * 定义processor支持的选项 * @return processor支持选项的集合 */ public Collection<String> supportedOptions(){ //返回空集合 return emptySet(); } public static void main(String[] argv){ System.out.println("ok"); }}package com.bjinfotech.practice.annotation.apt;import com.sun.mirror.apt.AnnotationProcessor;import com.sun.mirror.apt.AnnotationProcessorEnvironment;import com.sun.mirror.declaration.TypeDeclaration;import com.sun.mirror.util.DeclarationVisitors;import com.sun.mirror.util.DeclarationVisitor;/** * 定义Review annotation的Processor * @author cleverpig * */public class ReviewProcessor implements AnnotationProcessor{ //Processor所工作的环境 AnnotationProcessorEnvironment env=null; /** * 构造方法 * @param env 传入processor环境 */ public ReviewProcessor(AnnotationProcessorEnvironment env){ this.env=env; } /** * 处理方法:查询processor环境中的类型声明, */ public void process(){ //查询processor环境中的类型声明 for(TypeDeclaration type:env.getSpecifiedTypeDeclarations()){ //返回对类进行扫描、访问其声明时使用的DeclarationVisitor, //传入参数:new ReviewDeclarationVisitor(),为扫描开始前进行的对类声明的处理 // DeclarationVisitors.NO_OP,表示在扫描完成时进行的对类声明不做任何处理 DeclarationVisitor visitor=DeclarationVisitors.getDeclarationScanner( new ReviewDeclarationVisitor(),DeclarationVisitors.NO_OP); //应用DeclarationVisitor到类型 type.accept(visitor); } }}package com.bjinfotech.practice.annotation.apt;import com.sun.mirror.util.*;import com.sun.mirror.declaration.*;/** * 定义Review annotation声明访问者 * @author cleverpig * */public class ReviewDeclarationVisitor extends SimpleDeclarationVisitor{ /** * 定义访问类声明的方法:打印类声明的全名 * @param cd 类声明对象 */ public void visitClassDeclaration(ClassDeclaration cd){ System.out.println("获取Class声明:"+cd.getQualifiedName()); } public void visitAnnotationTypeDeclaration(AnnotationTypeDeclaration atd){ System.out.println("获取Annotation类型声明:"+atd.getSimpleName()); } public void visitAnnotationTypeElementDeclaration(AnnotationTypeElementDeclaration aed){ System.out.println("获取Annotation类型元素声明:"+aed.getSimpleName()); }}E:rem 项目根目录set PROJECT_ROOT=E:\eclipse3.1RC3\workspace\tigerFeaturePracticerem 包目录路径set PACKAGEPATH=com\bjinfotech\practice\annotation\aptrem 运行根路径set RUN_ROOT=%PROJECT_ROOT%\buildrem 源文件所在目录路径set SRC_ROOT=%PROJECT_ROOT%\testrem 设置Classpathset CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%cd %SRC_ROOT%\%PACKAGEPATH%apt -nocompile -factory com.bjinfotech.practice.annotation.apt.ReviewProcessorFactory ./*.java