MetaData Programme
1.1.        什么是元数据编程
什么是元数据,元数据就是描述数据的数据(data about data)。最明显的例子是XML Schema,xml schema就是描述xml的数据,所以它是元数据。另一个例子是数据库,比如我们可以查询数据库中有几个表,每个表都有什么字段,这些数据就是元数据。Office:office" />
在开发的世界里,元数据就是能够绑定到一个类的附加信息,在静态或者运行时间。JCR175给我们提供annotation就是一种元数据。
不过在这之前一个我们已经广泛使用的元数据是XML,如就是EJB的XML发布描述符中,你需要定义基于每一个方法的事务属性。应用服务器指导什么时候,什么地方开始,挂起或者提交一个事务,因为你在BEAN的XML的配置文件中的元数据内已经定义如方法:Required,RequiresNew,Support等等,它们绑定在你的EJB类和事务管理之间。XDoclet是另一个元数据的例子。
1.2.        Annotation的意义和简单例子
       JDK1.5提供的annotation与我们所常见的classes、fieldss和methods间是什么关系。如下:如果说类和数据成员是名词,方法是动词,那么annotation就是形容词或者副词,分别描述它们的所具有属性。
   好,现在就来实现一个annotation
 
 import Java.lang.annotation.Retention;
import Java.lang.annotation.Retention;

 package sample.annotation;
package sample.annotation;

 @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)


 public @interface Broker
    public @interface Broker  {
{

 String name();
    String name();

 String address();
    String address();

 }
    }

 }
}

 
使用这个annotation
 Import sample.annotation.broker;
Import sample.annotation.broker;

 @Broker(name="anders", address="xiamen")
@Broker(name="anders", address="xiamen")


 public class Agent
public class Agent  {
{


 public String getTelPhone ()
    public String getTelPhone () {
{

 return "010-0592-2519280";
        return "010-0592-2519280";

 }
    }

 }
}


运行期得到这个annotation

 public class Main
public class Main  {
{


 public static void main(String[] args)
    public static void main(String[] args) {
{

 Agent agent = new Agent();
Agent agent = new Agent();


 try
        try {
{

 Annotation[] a = agent.getClass().getMethod("getBrokerName").getAnnotations();
            Annotation[] a = agent.getClass().getMethod("getBrokerName").getAnnotations();


 for (int i=0; i<a.length ; i++)
            for (int i=0; i<a.length ; i++)  {
{


 if( a[i] instanceof Broker)
                 if( a[i] instanceof Broker) {
{

 Broker broker = (Broker)a[i];
                      Broker broker = (Broker)a[i];

 System.out.println(broker.name());
                     System.out.println(broker.name());

 }
                 }

 }
             }

 }
        }


 catch(Exception e)
        catch(Exception e) {
{

 e.printStackTrace(System.out);
            e.printStackTrace(System.out);

 }
        }

 }
   }

 }
}


1.3.        Annotation的class文件格式
       利用sun公司的提供的javap,我们可以看到annotation的在class文件中的表示。以下为对比结果:
源码:
 
 @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)


 public @interface Broker
public @interface Broker  {
{

 String name();
    String name();

 String address();
    String address();

 }
}

 
Javap结果:
Compiled from "Broker.java"
interface Broker extends java.lang.annotation.Annotation
 SourceFile: "Broker.java"
 minor version: 0
 major version: 0
 Constant pool:
const #1 = class    #9; // Broker
const #2 = class    #10; // Object
const #3 = class    #11; //Annotation
const #4 = Asciz    name;
const #5 = Asciz   ()Ljava/lang/String;;
const #6 = Asciz    address;
const #7 = Asciz    SourceFile;
const #8 = Asciz    Broker.java;
const #9 = Asciz    Broker;
const #10 = Asciz   java/lang/Object;
const #11 = Asciz   java/lang/annotation/Annotation;
{
public abstract java.lang.String name();
public abstract java.lang.String address();
}   
源码:
 @Broker(name="anders", address="xiamen")
@Broker(name="anders", address="xiamen")


 public class Agent
public class Agent  {
{


 public String getTelPhone()
    public String getTelPhone() {
{

 return "0592-2519580";
        return "0592-2519580";

 }
    }

 }
}


Javap结果:
Compiled from "Agent.java"
public class Agent extends java.lang.Object
 SourceFile: "Agent.java"
 RuntimeVisibleAnnotations: length = 0x10
   00 01 00 11 00 02 00 12 73 00 13 00 14 73 00 15
   
 minor version: 0
 major version: 0
 Constant pool:
const #1 = Method   #4.#22;// java/lang/Object."<init>":()V
const #2 = String   #23;    // 0592-2519580
const #3 = class    #24;    // Agent
const #4 = class    #25;    // Object
const #5 = Asciz    <init>;
const #6 = Asciz    ()V;
const #7 = Asciz    Code;
const #8 = Asciz    LineNumberTable;
const #9 = Asciz    LocalVariableTable;
const #10 = Asciz   this;
const #11 = Asciz   LAgent;;
const #12 = Asciz   getTelPhone;
const #13 = Asciz   ()Ljava/lang/String;;
const #14 = Asciz   SourceFile;
const #15 = Asciz   Agent.java;
const #16 = Asciz   RuntimeVisibleAnnotations;
const #17 = Asciz   LBroker;;
const #18 = Asciz   name;
const #19 = Asciz   anders;
const #20 = Asciz   address;
const #21 = Asciz   xiamen;
const #22 = NameAndType#5:#6;// "<init>":()V
const #23 = Asciz   0592-2519580;
const #24 = Asciz   Agent;
const #25 = Asciz   java/lang/Object;
// 以下为方法域,略   
补充说明:我们都知道在java 1.0发布时,java class file的格式就已经定下来,要说明的是为了应未来的需要java class file设计了属性说的机制。一直到J2SE1.4都没有怎么改变。但这次为了更好的支持metadata技术,一共增加了8个属性。分別是:
「EnclosingMethod」Attribute :Anonymous Class 或 Local Inner Class 使用此 Attribute 描述该Class 的Scope。
「Signature」 Attribute:Generics 的 Class、Method、或 Filed使用此 Attribute 来记录所要的类型,因为java的范型采用了擦拭法。
「LocalVariableTypeTable」 Attribute:主要是給 debugger 使用,目的和「LocalVariableTable」 Attribute类似,只是「LocalVariableTable」 Attribute 记录所要的参数表,而「LocalVariableTypeTable」 Attribute 记录参数的类型。
「RuntimeVisibleAnnotations」 Attribute:确定该annotation可以被reflection的API返回,适用对象:Class、Method、Field
「RuntimeInvisibleAnnotations」 Attribute:确定该annotation无法被reflection的API返回,适用对象: Class、Method、Field。 
「RuntimeVisibleParameterAnnotations」 Attribute:同「RuntimeVisibleAnnotations」 Attribute,适用对象:Method,(该Method 的参数
「RuntimeInvisibleParameterAnnotations」 Attribute:同「RuntimeInvisibleAnnotations」 Attribute,适用对象:Method,(该Method 的参数。
「AnnotationDefault」 Attribute:适用对象:Method,记录默认值。
1.4.        为什么需要Annotation
       在annotation之前我们已经广泛使用另外一种元数据xml,为什么需要annotation。Annotation与xml的作为元数据的区别是什么——位置。Annotation写在源代码中,而xml写在外部。
       为什么要这样?如果你开发过EJB,你一定为你的EJB写过xml描述文件。当大量的EJB需要描述时,就出现了所谓的"descriptor hell"。这个也导致了著名的XDoclet的出现。而annotation出现可以避免这种descriptor hell。另外你更改了某个方法为其增加或者减少一个参数,你就对应的修改xml文件,而使用annotation则不必。使用annotation将开发和部署更方便,提供开发效率。
       另外:使用xml的另一个问题是:很多Xml配置太过verbose。相比较EJB和Hibernate 或者Webwork可以明显的发现不同。
1.5.        再议Annotation
在EJB3中,Annotation把开发和部署的工作合在一起。但是在一些企业环境中,开发人员并不控制诸如数据源名等(这些是部署部门和管理部门的工作),这样把数据源名写在xml中将比较好。
       Annotation是本身静态的,一旦添加或者修改annotation都需要重新编译,在运行时读取,这样就丧失了运行时配置的能力。因此Annotations 不会取代xml,它只是提供了另一种途径。并且我相信sun公司将在未来提供一个方式可以在运行期更改metadata。
关于这点TSS上有着很激烈的讨论,很多开发人员提出:利用xml来更改annotation,并希望类似的方式能被采纳为标准规范。比如使用如下格式:
 
 <class name="org.hibernate.Item">
<class name="org.hibernate.Item">

 @Entity
@Entity

 @Table(name="AUCTION_ITEM")
    @Table(name="AUCTION_ITEM")

 <method sig="getBar()">@Transient</method>
    <method sig="getBar()">@Transient</method>

 <method sig="doSomething(int, String)">
    <method sig="doSomething(int, String)">

 @Tx(REQUIRES_NEW)
        @Tx(REQUIRES_NEW)

 </method>
    </method>

 </class>
</class>

 
 当然也有不同意见:But then, I think of "overriding" as a different problem to "xml deployment descriptors", and so I think we need two solutions. I think Cedric and Bill are trying to kill two birds with one stone, so maybe their proposals are better....
关于为annotation提供动态配置能力的问题,其中一个网友认为:Sun make it real pain to do the deployment XML so that they can introduce annotation to fix it. The annotation can make code/deployment coupling so strong that Sun can come out with a new way (annotation interceptor in jdk 1.6? :)) for fixing it. and the cycles goes on...这让我想起了类似的现象:JSP和TagLib。希望Annotation不会和TagLib有同样的命运。
       Annotation本身引入另一种类型的接口。在EJB3中确实使程序更加POJO,也消除了一些接口。并且编译后的代码也可以直接移植到另一个并不处理这些annotations的环境中(感谢VM在加载类时并不检查那些annotations的classes,甚至这些类不在classpath中)。然而代码也确实增加了另一些接口。这个表现在编译期,如果没有这些annotation classes,是编译不过的。
       另一个问题(还好不算很重要),关于annotation的namespace。在多层应用的系统中,可能会出现同样的全局annotation的namespace冲突。比如一些公共的annotation,如@Transaction,将会被应用在很多个层次中。尽量让namespace长些,可以避免这个问题。
1.6.        元数据编程的应用:
Annotation已经被集成到很多的java规范和标准中,很重要的是它已经被J2EE标准,如EJB3所采用,当然也被许多开源的组件体系如:ASPectJ。
       Annotation最重要的应用将是AOP:由于annotation可以天然的表示系统中的另一个横切面,同时Annotation的识别是通过反射得到的,所以Annotation很自然的应用到基于动态代理的AOP实现。AOP-Alliance也支持metadata handling。AspectJ也发布了基于annotation的新版本。
       在实现AOP上,使用annotation也比使用XML有一个优势:如前所述,annotation更像是形容词和副词,这样比较不容易verbose。当然这个是相对的,在实际的实现中更依赖开发人员的努力。
       这里,笔者将展示一个不完整也不成熟的基于annotation的AOP例子代码——关于银行卡的例子。
       功能需求:银行卡业务分为转帐,查询余额,查询明细,POS消费等。这其中转帐和POS消费是要收费的(转帐收取的是用户的手续费,而POS消费收取的是商家的手续费),另外POS消费还可能有积分的(比如笔者的牡丹贷记卡)。消费转帐都要记录明细。但查询余额就不需要记录在明细中。
       代码如下(在这个例子没有用动态代理也没有用已有的AOP框架,使代码看起来简单些)
  1 // 银行卡对象
// 银行卡对象
  2
  3
 public class Card
public class Card  {
{
  4
  5 private String account;
    private String account; 
  6
  7 //some field method
//some field method
  8
  9 }
}
 10
 11 Annotation:
Annotation:
 12
 13 // 手续费
// 手续费
 14
 15 // type= "user", 表示收取用户手续费; type= "Biz", 表示收取商家手续费
// type= "user", 表示收取用户手续费; type= "Biz", 表示收取商家手续费
 16
 17
 public @interface Fee
public @interface Fee {
{
 18
 19 String type();
    String type();
 20
 21 }
}
 22
 23 // 积分
// 积分
 24
 25
 public @interface Index
public @interface Index  {
{
 26
 27 }
}
 28
 29 // 记录明细
// 记录明细
 30
 31
 public @interface BizLog
public @interface BizLog  {
{
 32
 33 }
}
 34
 35 // 事务处理
// 事务处理
 36
 37
 public @interface Transaction
public @interface Transaction  {
{
 38
 39 }
}
 40
 41 // 业务接口
// 业务接口
 42
 43
 public interface BizAction
public interface BizAction  {
{
 44
 45 void execute(Card card, RunData rundata);
    void execute(Card card, RunData rundata);
 46
 47 }
}
 48
 49 // 转帐业务
// 转帐业务
 50
 51 @Fee(type="user")
@Fee(type="user")
 52
 53 @Transaction
@Transaction
 54
 55 @BizLog
@BizLog
 56
 57
 public class TransferAction implements BizAction
public class TransferAction implements BizAction  {
{
 58
 59
 public void execute(Card card, RunData rundata)
    public void execute(Card card, RunData rundata)  {
{
 60
 61 //To change body of implemented methods use File | Settings | File Templates.
        //To change body of implemented methods use File | Settings | File Templates.
 62
 63 }
    }
 64
 65 }
}
 66
 67 // POS消费
// POS消费
 68
 69 @Fee(type="Biz")
@Fee(type="Biz")
 70
 71 @Transaction
@Transaction
 72
 73 @BizLog
@BizLog
 74
 75 @Index
@Index
 76
 77
 public class POSAction implements BizAction
public class POSAction implements BizAction  {
{
 78
 79
 public void execute(Card card, RunData rundata)
    public void execute(Card card, RunData rundata)  {
{
 80
 81 //To change body of implemented methods use File | Settings | File Templates.
        //To change body of implemented methods use File | Settings | File Templates.
 82
 83 }
    }
 84
 85 }
}
 86
 87 // 查询明细
// 查询明细
 88
 89
 public class QueryDetail implements BizAction
public class QueryDetail implements BizAction  {
{    
 90
 91
 public void execute(Card card, RunData rundata)
    public void execute(Card card, RunData rundata)  {
{
 92
 93 //To change body of implemented methods use File | Settings | File Templates.
        //To change body of implemented methods use File | Settings | File Templates.
 94
 95 }
    }
 96
 97 }
}
 98
 99 // 业务操作监视器接口
// 业务操作监视器接口
100
101
 public interface BizActionMonitor
public interface BizActionMonitor  {
{
102
103 void execute(BizAction action, RunData rundata);
   void execute(BizAction action, RunData rundata);
104
105 }
}
106
107 // 业务操作监视器实现
// 业务操作监视器实现
108
109
 public class BizActionMonitorImpl implements BizActionMonitor
public class BizActionMonitorImpl implements BizActionMonitor {
{
110
111
 public void execute(BizAction action, RunData rundata)
    public void execute(BizAction action, RunData rundata)  {
{
112
113 Annotation[] annotations = action.getClass().getAnnotations();
        Annotation[] annotations = action.getClass().getAnnotations();
114
115
 for(Annotation annotation : annotations)
        for(Annotation annotation : annotations) {
{
116
117
 if (annotation instanceof Fee)
            if (annotation instanceof Fee) { // 计算手续费   }
{ // 计算手续费   }
118
119
 if (annotation instanceof Index)
            if (annotation instanceof Index) { //计算积分    }
{ //计算积分    }
120
121
 if (annotation instanceof Transaction)
            if (annotation instanceof Transaction) { // 准备事务 }
{ // 准备事务 }
122
123
 if (annotation instanceof BizLog)
            if (annotation instanceof BizLog) { // 记录明细   }
{ // 记录明细   }
124
125 }
        }
126
127 }
    }
128
129 }
}
130
131 // 控制器对象
// 控制器对象
132
133
 public class controller
public class controller {
{
134
135 private BizActionMonitor monitor;
    private BizActionMonitor monitor;   
136
137
 public void execute(BizActionUI, rundata)
    public void execute(BizActionUI, rundata) {
{
138
139 BizAction action = getAction(BizActionUI);
        BizAction action = getAction(BizActionUI);      
140
141 monitor.execute(action, rundata);
        monitor.execute(action, rundata);
142
143 }
    }
144
145 }
}
146
147 // 运行时数据
// 运行时数据
148
149
 public interface RunData
public interface RunData  {
{
150
151 // some method
   // some method
152
153 }
}
154
155 // 用户设备(POS机, ATM或者柜台终端)接口
// 用户设备(POS机, ATM或者柜台终端)接口
156
157
 public class BizActionUI
public class BizActionUI  {
{
158
159 private RunData rundata;
      private RunData rundata;
160
161 private Controller controller;
      private Controller controller;
162
163
 public BizActionUI(RunData rundata, Controller controller)
      public BizActionUI(RunData rundata, Controller controller) {
{
164
165 this.rundata = rundata;
          this.rundata = rundata;
166
167 this.controller = controller;
          this.controller = controller;
168
169 }
      }
170
171
 public void execute()
      public void execute() { // 某个子类实现 }
{ // 某个子类实现 }
172
173
 public void commit()
      public void commit() {
{
174
175 controller.execute(this, rundata);
          controller.execute(this, rundata);
176
177 }
      }
178
179 }
}
180
181
 public class Main
public class Main {
{
182
183 private Rundata rundata;
    private Rundata rundata;
184
185 private Controller controller;
    private Controller controller; 
186
187
 public populateUI(command)
    public populateUI(command) {
{
188
189 BizActionUI ui = getUI(command);
        BizActionUI ui = getUI(command);
190
191 ui.execute();
        ui.execute();
192
193 }
    }   
194
195
 public BizActionUI getUI(command)
    public BizActionUI getUI(command) {
{
196
197 //
        //
198
199 BizActionUI ui
        BizActionUI ui 
200
201 if( //
        if( // .){
.){
202
203 ui = new SomeBizActionUI(rundata, controller);
            ui = new SomeBizActionUI(rundata, controller);
204
205 }
        }
206
207 return ui;
        return ui;      
208
209 }
    }    
210
211
 public static main(String[] args)
    public static main(String[] args) {
{
212
213 //
        //
214
215 Main main = new Main();
        Main main = new Main();
216
217 main.populateUI(command)
        main.populateUI(command)
218
219 //
        //
220
221 }
    }
222
223 }
}
224
225
 
1.7.        结束语:
本文讨论了annotation技术,展示了annotation的class文件格式,并讨论了annotation技术本身的优势和不足,并于现有的xml技术加以比较,展望了annotation技术的应用前景AOP。
限于笔者自身的水平(包括技术水平和写作水平),技术上对annotation的学习比较有限,写作上也感觉好多话无法用文字来表达,因而本文的代码会比较多(大概有一半以上)。