MetaData Programme
1.1. 什么是元數據編程
什么是元數據,元數據就是描述數據的數據(data about data)。最明顯的例子是XML Schema,xml schema就是描述xml的數據,所以它是元數據。另一個例子是數據庫,比如我們可以查詢數據庫中有幾個表,每個表都有什么字段,這些數據就是元數據。Office:office" />
在開發的世界里,元數據就是能夠綁定到一個類的附加
不過在這之前一個我們已經廣泛使用的元數據是XML,如就是EJB的XML發布描述符中,你需要定義基于每一個方法的事務屬性。
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寫在外部。
為什么要這樣?如果你
另外:使用xml的另一個問題是:很多Xml配置太過verbose。相比較EJB和Hibernate 或者Webwork可以明顯的發現不同。
1.5. 再議Annotation
在EJB3中,Annotation把開發和部署的工作合在一起。但是在一些企業環境中,開發人員并不控制諸如數據源名等(這些是部署部門和管理部門的工作),這樣把數據源名寫在xml中將比較好。
Annotation是本身靜態的,一旦添加或者修改annotation都需要重新編譯,在運行時讀取,這樣就喪失了運行時配置的
關于這點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的學習比較有限,寫作上也感覺好多話無法用文字來表達,因而本文的代碼會比較多(大概有一半以上)。