在 创建EJB组件时,必需提供一些定义,使得EJB组件使用一些服务例如:安全服务,持久化服务,事务服务。EJB容器可以提供这些服务,这样EJB只要实 现业务逻辑就可以了。但是说到底EJB容器使用EJB组件的元数据来提供这些服务,在以前EJB的元数据是以XML配置文件形式出现的,这些配置文件与 EJB源文件是分开的。
EJB的部署人员无法了解EJB本身的信息,如果EJB组件的创建者用注释(Annotation)的方法将这些配置服务的信息和代码放在一起,这样 EJB的部署者就可以了解EJB的信息,EJB的home接口可以使用Annotation自动生成,当然到目前为止更好的是在简单的Java Object上使用Annotations。
一 什么是Annotation
在已经发布的JDK1.5(tiger)中增加新的特色叫 Annotation。Annotation提供一种机制,将程序的元素如:类,方法,属性,参数,本地变量,包和元数据联系起来。这样编译器可以将元数 据存储在Class文件中。这样虚拟机和其它对象可以根据这些元数据来决定如何使用这些程序元素或改变它们的行为。
二 定义一个简单的Annotation并使用它
1.定义Annotation
定义一个Annotation是什么简单的,它采取的是类似于Interface的定义方式: “@+annotation类型名称+(..逗号分割的name-value对...)”
- //Example 1
- package sz.starbex.bill.annotation;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import java.lang.annotation.ElementType;
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface SimpleAnnotation {
- String value();
- }
@Retention这个meta-annotation表示我们创建的SimpleAnnotation这个Annotation将会存储在Class文件中,并在java
VM运行时加载它。@Target这个meta-annotation表示我们创建的SimplwAnnotation将会为描述方法,而 @interface SimpleAnnotation是我们自定义的Annotation,它有一个成员叫value,返回值是String。
2.使用Annotation
- //Example 2
- package sz.starbex.bill.annotation;
- import sz.starbex.bill.annotation.SimpleAnnotation;
- public class UsingSimpleAnnotation {
- @SimpleAnnotation(value="Pass:This method will Pass")//注意name=value的用法
- public void pass(){
- if(10>5) System.out.println("测试通过");
- }
- @SimpleAnnotation("Fail:This method will Fail")//注意name=value的用法
- public void fail(){
- if(10<5) System.out.println("测试失败");
- }
- }
一个Annotation用于程序元素(在本例中是method),在method方法之前用(@Annotation名称(name=value, name=value.....)。在本例中是@SimpleAnnotation(value="Pass:This method will Pass")。每个annotation具有一个名字和成员个数>=0,当只有一个单一的成员时,这个成员就是value。我们也可以这样写 @SimpleAnnotation("Fail:This method will Fail")。至此@SimpleAnnotation将Pass和Fail联系起来了。
3.在运行时访问Annotation
一旦Annotation与程序元素联系起来,我们可以通过反射访问它们并可以取得它们的值。我们使用一个新的interface: java.lang.reflect.AnnotatedElement。java.lang.reflect.AnnotatedElement接口中 的方法有:
a. boolean isAnnotationPresent(Class annotationType)
如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
b. T getAnnotation(Class annotationType)
如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
c. Annotation[] getAnnotations()
返回此元素上存在的所有注释。
d. Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。
你要注意 isAnnotationPresent和getAnnotation方法,它们使用了Generics,请参考我的Java 范型的Blog。
下面我们列出一些实现了AnnotatedElement 接口的类
1. java.lang.reflect.AccessibleObject
2. java.lang.Class
3. java.lang.reflect.Constructor
4. java.lang.reflect.Field
5. java.lang.reflect.Method
6. java.lang.Package
下面的Example程序说明了如何在运行环境访问Annotation
- package sz.starbex.bill.annotation;
- import sz.starbex.bill.annotation.SimpleAnnotation;
- import java.lang.reflect.Method;
- public class SimpleAccessAnnotation {
- static void accessAnnotationTest(Class usingAnnnotationClass){
- try {
- //Object usingAnnnotationClass=Class.forName(usingAnnotationClassName).newInstance();
- Method [] methods=usingAnnnotationClass.getDeclaredMethods();//取得对方法
- for(Method method:methods){
- System.out.println(method.getName());
- SimpleAnnotation
- simpleAnnotation=method.getAnnotation(SimpleAnnotation.class);//得到方法的Annotation
- if(simpleAnnotation!=null){
- System.out.print(simpleAnnotation.value()+"==");
- String result=invoke(method,usingAnnnotationClass);
- System.out.println(result);
- }
- }
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- static String invoke(Method m, Object o) {
- String result = "passed";
- try {
- m.invoke(m,new Object[]{});
- } catch (Exception e) {
- // TODO Auto-generated catch block
- result = "failed";
- }
- return result;
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- accessAnnotationTest(UsingSimpleAnnotation.class);
- }
- }
Java 中的Annotation的定义
Java中的Annotation
Java定义了几个标准的meta-annotation,在新Package中java.lang.annotation 中包含了以下meta-annotation:
meta-annotation 说明
@Target
1. annotation的target是一个被标注的程序元素。target说明了annotation所修饰的对象范围:annotation可被用于 packages、types(类、接口、枚举、annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循 环变量、catch参数)。在annotation类型的声明中使用了target可更加明晰其修饰的目标。
meta-annotation 说明
@Target 1. annotation的target是一个被标注的程序元素。target说明了annotation所修饰的对象范围:annotation可被用于 packages、types(类、接口、枚举、annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循 环变量、catch参数)。在annotation类型的声明中使用了target可更加明晰其修饰的目标。
2. ElementType的定义
TYPE// Class, interface, or enum (but not annotation)
FIELD// Field (including enumerated values)
METHOD// Method (does not include constructors)
PARAMETER// Method parameter
CONSTRUCTOR// Constructor
LOCAL_VARIABLE// Local variable or catch clause
ANNOTATION_TYPE// Annotation Types (meta-annotations)
PACKAGE// Java package
@Retention
1. SOURCE//按照规定使用注释,但是并不将它保留到编译后的类文件中
2. CLASS//将注释保留在编译后的类文件中,但是在运行时忽略它
3. RUNTIME//将注释保留在编译后的类文件中,并在第一次加载类时读取它
@Documented Documented 表示注释应该出现在类的 Javadoc 中
@Inherited 一个Annotation将被继承
三个标准的Annotation 在java.lang包中:
@Deprecated 对不再使用的方法进行注释
@Override 指明注释的方法覆盖超类的方法
@SuppressWarnings 阻止编译器的警告,例:当类型不安全时
下例来说明这三个标准的Annotation:
- package sz.starbex.bill.annotation;
- import java.util.ArrayList;
- import java.util.List;
- public class SimpleOverrideAnnotation {
- public static void main(String[] args) {
- SimpleOverrideAnnotation test = new SimpleOverrideAnnotation();
- System.out.println(test.toString());
- }
- @Override
- public String toString() {
- return "自己的类自己输出";
- }
- @Deprecated
- public void doSomething() {
- System.out.println("方法已过时" );
- }
- @SuppressWarnings(value={"unchecked"})
- public void testSuppressWarnings(){
- List testList=new ArrayList();
- testList.add("KKKK");//没有使用范型,类型不安全
- }
- }
二、Annotation使用实例
一个组合的Annotation,注释类的
a. 商标Annotation
- package sz.starbex.bill.annotation;
- public @interface Trademark {
- String name();
- String owner();
- }
b.License的annotation
- package sz.starbex.bill.annotation;
- import java.lang.annotation.*;
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE, ElementType.PACKAGE})
- public @interface License {
- String name();
- String notice();
- boolean redistributable();
- Trademark[] trademarks();
- }
c.测试类
- package sz.starbex.bill.annotation;
- @License(name="Bill",
- notice="许可证",
- redistributable=true,
- trademarks={@Trademark(name="Mercedes",owner="Swedish"),
- @Trademark(name="Daewoo",owner="Korean")
- }
- )
- public class TestLicenseAnnotation {
- public static void main(String[] args) {
- TestLicenseAnnotation test=new TestLicenseAnnotation();
- License license=test.getClass().getAnnotation(License.class);
- System.out.println("License发放人:"+license.name());
- System.out.println("License注意事项:"+license.notice());
- System.out.println("License许可:"+license.redistributable());
- Trademark [] marks=license.trademarks();
- for(Trademark mark:marks){
- System.out.println("商标名称:"+mark.name());
- System.out.println("商标的使用者:"+mark.owner());
- }
- }
- }
前不久在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 "";
}
B.Exportable类:
清单2:
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 "";
}
C.AddressForTest类:
清单3:
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;
}
}
D.AddressListForTest类:
清单4:
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;
}
}
E.ExportToXml类:
清单5:
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();
}
}
}
在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:
<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>
三、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:
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";
}
C.ReviewProcessorFactory类:
清单8:
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;
//请注意为了方便,使用了静态import
import 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");
}
}
D.ReviewProcessor类:
清单9:
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);
}
}
}
E.ReviewDeclarationVisitor类:
清单10:
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());
}
}
F.runapt.bat文件内容如下:
清单11:
E:
rem 项目根目录
set PROJECT_ROOT=E:/eclipse3.1RC3/workspace/tigerFeaturePractice
rem 包目录路径
set PACKAGEPATH=com/bjinfotech/practice/annotation/apt
rem 运行根路径
set RUN_ROOT=%PROJECT_ROOT%/build
rem 源文件所在目录路径
set SRC_ROOT=%PROJECT_ROOT%/test
rem 设置Classpath
set CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%
cd %SRC_ROOT%/%PACKAGEPATH%
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