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

使用这个annotation


运行期得到这个annotation
public
class
Main
{
![]()
![]()
public static void main(String[] args)
{
![]()
Agent agent = new Agent();
![]()
![]()
try
{
![]()
Annotation[] a = agent.getClass().getMethod("getBrokerName").getAnnotations();
![]()
![]()
for (int i=0; i<a.length ; i++)
{
![]()
![]()
if( a[i] instanceof Broker)
{
![]()
Broker broker = (Broker)a[i];
![]()
System.out.println(broker.name());
![]()
}
![]()
}
![]()
}
![]()
![]()
catch(Exception e)
{
![]()
e.printStackTrace(System.out);
![]()
}
![]()
}
![]()
}
![]()
1.3. Annotation的class文件格式






利用sun公司的提供的javap,我们可以看到annotation的在class文件中的表示。以下为对比结果:
源码:

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();
}
源码:


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,并希望类似的方式能被采纳为标准规范。比如使用如下格式:
当然也有不同意见: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
{
4![]()
5
private String account;
6![]()
7
//some field method
8![]()
9
}
10
![]()
11
Annotation:
12
![]()
13
//
手续费
14
![]()
15
//
type= "user", 表示收取用户手续费; type= "Biz", 表示收取商家手续费
16
17
![]()
public
@
interface
Fee
{
18![]()
19
String type();
20![]()
21
}
22
![]()
23
//
积分
24
25
![]()
public
@
interface
Index
{
26![]()
27
}
28
![]()
29
//
记录明细
30
31
![]()
public
@
interface
BizLog
{
32![]()
33
}
34
![]()
35
//
事务处理
36
37
![]()
public
@
interface
Transaction
{
38![]()
39
}
40
![]()
41
//
业务接口
42
43
![]()
public
interface
BizAction
{
44![]()
45
void execute(Card card, RunData rundata);
46![]()
47
}
48
![]()
49
//
转帐业务
50
51
@Fee(type
=
"
user
"
)
52
![]()
53
@Transaction
54
![]()
55
@BizLog
56
![]()
57
![]()
public
class
TransferAction
implements
BizAction
{
58![]()
59![]()
public void execute(Card card, RunData rundata)
{
60![]()
61
//To change body of implemented methods use File | Settings | File Templates.
62![]()
63
}
64![]()
65
}
66
![]()
67
//
POS消费
68
69
@Fee(type
=
"
Biz
"
)
70
![]()
71
@Transaction
72
![]()
73
@BizLog
74
![]()
75
@Index
76
![]()
77
![]()
public
class
POSAction
implements
BizAction
{
78![]()
79![]()
public void execute(Card card, RunData rundata)
{
80![]()
81
//To change body of implemented methods use File | Settings | File Templates.
82![]()
83
}
84![]()
85
}
86
![]()
87
//
查询明细
88
89
![]()
public
class
QueryDetail
implements
BizAction
{
90![]()
91![]()
public void execute(Card card, RunData rundata)
{
92![]()
93
//To change body of implemented methods use File | Settings | File Templates.
94![]()
95
}
96![]()
97
}
98
![]()
99
//
业务操作监视器接口
100
101
![]()
public
interface
BizActionMonitor
{
102![]()
103
void execute(BizAction action, RunData rundata);
104![]()
105
}
106
![]()
107
//
业务操作监视器实现
108
109
![]()
public
class
BizActionMonitorImpl
implements
BizActionMonitor
{
110![]()
111![]()
public void execute(BizAction action, RunData rundata)
{
112![]()
113
Annotation[] annotations = action.getClass().getAnnotations();
114![]()
115![]()
for(Annotation annotation : annotations)
{
116![]()
117![]()
if (annotation instanceof Fee)
{ // 计算手续费 }
118![]()
119![]()
if (annotation instanceof Index)
{ //计算积分 }
120![]()
121![]()
if (annotation instanceof Transaction)
{ // 准备事务 }
122![]()
123![]()
if (annotation instanceof BizLog)
{ // 记录明细 }
124![]()
125
}
126![]()
127
}
128![]()
129
}
130![]()
131
// 控制器对象
132![]()
133![]()
public class controller
{
134![]()
135
private BizActionMonitor monitor;
136![]()
137![]()
public void execute(BizActionUI, rundata)
{
138![]()
139
BizAction action = getAction(BizActionUI);
140![]()
141
monitor.execute(action, rundata);
142![]()
143
}
144![]()
145
}
146![]()
147
// 运行时数据
148![]()
149![]()
public interface RunData
{
150![]()
151
// some method
152![]()
153
}
154![]()
155
// 用户设备(POS机, ATM或者柜台终端)接口
156![]()
157![]()
public class BizActionUI
{
158![]()
159
private RunData rundata;
160![]()
161
private Controller controller;
162![]()
163![]()
public BizActionUI(RunData rundata, Controller controller)
{
164![]()
165
this.rundata = rundata;
166![]()
167
this.controller = controller;
168![]()
169
}
170![]()
171![]()
public void execute()
{ // 某个子类实现 }
172![]()
173![]()
public void commit()
{
174![]()
175
controller.execute(this, rundata);
176![]()
177
}
178![]()
179
}
180![]()
181![]()
public class Main
{
182![]()
183
private Rundata rundata;
184![]()
185
private Controller controller;
186![]()
187![]()
public populateUI(command)
{
188![]()
189
BizActionUI ui = getUI(command);
190![]()
191
ui.execute();
192![]()
193
}
194![]()
195![]()
public BizActionUI getUI(command)
{
196![]()
197
//
198![]()
199
BizActionUI ui
200![]()
201
if( //
.){
202![]()
203
ui = new SomeBizActionUI(rundata, controller);
204![]()
205
}
206![]()
207
return ui;
208![]()
209
}
210![]()
211![]()
public static main(String[] args)
{
212![]()
213
//
214![]()
215
Main main = new Main();
216![]()
217
main.populateUI(command)
218![]()
219
//
220![]()
221
}
222![]()
223
}
224![]()
225
1.7. 结束语:
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25

26
27
28
29
30
31

32
33
34
35
36
37

38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

78
79

80
81
82
83
84
85
86
87
88
89

90
91

92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109

110
111

112
113
114
115

116
117

118
119

120
121

122
123

124
125
126
127
128
129
130
131
132
133

134
135
136
137

138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157

158
159
160
161
162
163

164
165
166
167
168
169
170
171

172
173

174
175
176
177
178
179
180
181

182
183
184
185
186
187

188
189
190
191
192
193
194
195

196
197

198
199
200
201

202
203
204
205
206
207
208
209
210
211

212
213

214
215
216
217
218
219

220
221
222
223
224
225
本文讨论了annotation技术,展示了annotation的class文件格式,并讨论了annotation技术本身的优势和不足,并于现有的xml技术加以比较,展望了annotation技术的应用前景AOP。
限于笔者自身的水平(包括技术水平和写作水平),技术上对annotation的学习比较有限,写作上也感觉好多话无法用文字来表达,因而本文的代码会比较多(大概有一半以上)。