[WEB] MetaData Programme
Posted on 2007-12-01 20:36 kooyee 閱讀(760) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): JavaMetaData Programme
1.1. 什么是元數(shù)據(jù)編程
什么是元數(shù)據(jù),元數(shù)據(jù)就是描述數(shù)據(jù)的數(shù)據(jù)(data about data)。最明顯的例子是XML Schema,xml schema就是描述xml的數(shù)據(jù),所以它是元數(shù)據(jù)。另一個(gè)例子是數(shù)據(jù)庫(kù),比如我們可以查詢(xún)數(shù)據(jù)庫(kù)中有幾個(gè)表,每個(gè)表都有什么字段,這些數(shù)據(jù)就是元數(shù)據(jù)。Office:office" />
在開(kāi)發(fā)的世界里,元數(shù)據(jù)就是能夠綁定到一個(gè)類(lèi)的附加
不過(guò)在這之前一個(gè)我們已經(jīng)廣泛使用的元數(shù)據(jù)是XML,如就是EJB的XML發(fā)布描述符中,你需要定義基于每一個(gè)方法的事務(wù)屬性。
1.2. Annotation的意義和簡(jiǎn)單例子
JDK1.5提供的annotation與我們所常見(jiàn)的classes、fieldss和methods間是什么關(guān)系。如下:如果說(shuō)類(lèi)和數(shù)據(jù)成員是名詞,方法是動(dòng)詞,那么annotation就是形容詞或者副詞,分別描述它們的所具有屬性。
好,現(xiàn)在就來(lái)實(shí)現(xiàn)一個(gè)annotation
















使用這個(gè)annotation















運(yùn)行期得到這個(gè)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文件中的表示。以下為對(duì)比結(jié)果:
源碼:










Javap結(jié)果:
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結(jié)果:
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;
// 以下為方法域,略
補(bǔ)充說(shuō)明:我們都知道在java 1.0發(fā)布時(shí),java class file的格式就已經(jīng)定下來(lái),要說(shuō)明的是為了應(yīng)未來(lái)的需要java class file設(shè)計(jì)了屬性說(shuō)的機(jī)制。一直到J2SE1.4都沒(méi)有怎么改變。但這次為了更好的支持metadata技術(shù),一共增加了8個(gè)屬性。分別是:
「EnclosingMethod」Attribute :Anonymous Class 或 Local Inner Class 使用此 Attribute 描述該Class 的Scope。
「Signature」 Attribute:Generics 的 Class、Method、或 Filed使用此 Attribute 來(lái)記錄所要的類(lèi)型,因?yàn)?/span>java的范型采用了擦拭法。
「LocalVariableTypeTable」 Attribute:主要是給 debugger 使用,目的和「LocalVariableTable」 Attribute類(lèi)似,只是「LocalVariableTable」 Attribute 記錄所要的參數(shù)表,而「LocalVariableTypeTable」 Attribute 記錄參數(shù)的類(lèi)型。
「RuntimeVisibleAnnotations」 Attribute:確定該annotation可以被reflection的API返回,適用對(duì)象:Class、Method、Field
「RuntimeInvisibleAnnotations」 Attribute:確定該annotation無(wú)法被reflection的API返回,適用對(duì)象: Class、Method、Field。
「RuntimeVisibleParameterAnnotations」 Attribute:同「RuntimeVisibleAnnotations」 Attribute,適用對(duì)象:Method,(該Method 的參數(shù)
「RuntimeInvisibleParameterAnnotations」 Attribute:同「RuntimeInvisibleAnnotations」 Attribute,適用對(duì)象:Method,(該Method 的參數(shù)。
「AnnotationDefault」 Attribute:適用對(duì)象:Method,記錄默認(rèn)值。
1.4. 為什么需要Annotation
在annotation之前我們已經(jīng)廣泛使用另外一種元數(shù)據(jù)xml,為什么需要annotation。Annotation與xml的作為元數(shù)據(jù)的區(qū)別是什么——位置。Annotation寫(xiě)在源代碼中,而xml寫(xiě)在外部。
為什么要這樣?如果你
另外:使用xml的另一個(gè)問(wèn)題是:很多Xml配置太過(guò)verbose。相比較EJB和Hibernate 或者Webwork可以明顯的發(fā)現(xiàn)不同。
1.5. 再議Annotation
在EJB3中,Annotation把開(kāi)發(fā)和部署的工作合在一起。但是在一些企業(yè)環(huán)境中,開(kāi)發(fā)人員并不控制諸如數(shù)據(jù)源名等(這些是部署部門(mén)和管理部門(mén)的工作),這樣把數(shù)據(jù)源名寫(xiě)在xml中將比較好。
Annotation是本身靜態(tài)的,一旦添加或者修改annotation都需要重新編譯,在運(yùn)行時(shí)讀取,這樣就喪失了運(yùn)行時(shí)配置的
關(guān)于這點(diǎn)TSS上有著很激烈的討論,很多開(kāi)發(fā)人員提出:利用xml來(lái)更改annotation,并希望類(lèi)似的方式能被采納為標(biāo)準(zhǔn)規(guī)范。比如使用如下格式:
















當(dāng)然也有不同意見(jiàn):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....
關(guān)于為annotation提供動(dòng)態(tài)配置能力的問(wèn)題,其中一個(gè)網(wǎng)友認(rèn)為: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...這讓我想起了類(lèi)似的現(xiàn)象:JSP和TagLib。希望Annotation不會(huì)和TagLib有同樣的命運(yùn)。
Annotation本身引入另一種類(lèi)型的接口。在EJB3中確實(shí)使程序更加POJO,也消除了一些接口。并且編譯后的代碼也可以直接移植到另一個(gè)并不處理這些annotations的環(huán)境中(感謝VM在加載類(lèi)時(shí)并不檢查那些annotations的classes,甚至這些類(lèi)不在classpath中)。然而代碼也確實(shí)增加了另一些接口。這個(gè)表現(xiàn)在編譯期,如果沒(méi)有這些annotation classes,是編譯不過(guò)的。
另一個(gè)問(wèn)題(還好不算很重要),關(guān)于annotation的namespace。在多層應(yīng)用的系統(tǒng)中,可能會(huì)出現(xiàn)同樣的全局annotation的namespace沖突。比如一些公共的annotation,如@Transaction,將會(huì)被應(yīng)用在很多個(gè)層次中。盡量讓namespace長(zhǎng)些,可以避免這個(gè)問(wèn)題。
1.6. 元數(shù)據(jù)編程的應(yīng)用:
Annotation已經(jīng)被集成到很多的java規(guī)范和標(biāo)準(zhǔn)中,很重要的是它已經(jīng)被J2EE標(biāo)準(zhǔn),如EJB3所采用,當(dāng)然也被許多開(kāi)源的組件體系如:ASPectJ。
Annotation最重要的應(yīng)用將是AOP:由于annotation可以天然的表示系統(tǒng)中的另一個(gè)橫切面,同時(shí)Annotation的識(shí)別是通過(guò)反射得到的,所以Annotation很自然的應(yīng)用到基于動(dòng)態(tài)代理的AOP實(shí)現(xiàn)。AOP-Alliance也支持metadata handling。AspectJ也發(fā)布了基于annotation的新版本。
在實(shí)現(xiàn)AOP上,使用annotation也比使用XML有一個(gè)優(yōu)勢(shì):如前所述,annotation更像是形容詞和副詞,這樣比較不容易verbose。當(dāng)然這個(gè)是相對(duì)的,在實(shí)際的實(shí)現(xiàn)中更依賴(lài)開(kāi)發(fā)人員的努力。
這里,筆者將展示一個(gè)不完整也不成熟的基于annotation的AOP例子代碼——關(guān)于銀行卡的例子。
功能需求:銀行卡業(yè)務(wù)分為轉(zhuǎn)帳,查詢(xún)余額,查詢(xún)明細(xì),POS消費(fèi)等。這其中轉(zhuǎn)帳和POS消費(fèi)是要收費(fèi)的(轉(zhuǎn)帳收取的是用戶(hù)的手續(xù)費(fèi),而POS消費(fèi)收取的是商家的手續(xù)費(fèi)),另外POS消費(fèi)還可能有積分的(比如筆者的牡丹貸記卡)。消費(fèi)轉(zhuǎn)帳都要記錄明細(xì)。但查詢(xún)余額就不需要記錄在明細(xì)中。
代碼如下(在這個(gè)例子沒(méi)有用動(dòng)態(tài)代理也沒(méi)有用已有的AOP框架,使代碼看起來(lái)簡(jiǎn)單些)
1
// 銀行卡對(duì)象
2
3
public class Card {
4
5
private String account;
6
7
//some field method
8
9
}
10
11
Annotation:
12
13
// 手續(xù)費(fèi)
14
15
// type= "user", 表示收取用戶(hù)手續(xù)費(fèi); type= "Biz", 表示收取商家手續(xù)費(fèi)
16
17
public @interface Fee{
18
19
String type();
20
21
}
22
23
// 積分
24
25
public @interface Index {
26
27
}
28
29
// 記錄明細(xì)
30
31
public @interface BizLog {
32
33
}
34
35
// 事務(wù)處理
36
37
public @interface Transaction {
38
39
}
40
41
// 業(yè)務(wù)接口
42
43
public interface BizAction {
44
45
void execute(Card card, RunData rundata);
46
47
}
48
49
// 轉(zhuǎn)帳業(yè)務(wù)
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消費(fèi)
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
// 查詢(xún)明細(xì)
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
// 業(yè)務(wù)操作監(jiān)視器接口
100
101
public interface BizActionMonitor {
102
103
void execute(BizAction action, RunData rundata);
104
105
}
106
107
// 業(yè)務(wù)操作監(jiān)視器實(shí)現(xiàn)
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){ // 計(jì)算手續(xù)費(fèi) }
118
119
if (annotation instanceof Index){ //計(jì)算積分 }
120
121
if (annotation instanceof Transaction){ // 準(zhǔn)備事務(wù) }
122
123
if (annotation instanceof BizLog){ // 記錄明細(xì) }
124
125
}
126
127
}
128
129
}
130
131
// 控制器對(duì)象
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
// 運(yùn)行時(shí)數(shù)據(jù)
148
149
public interface RunData {
150
151
// some method
152
153
}
154
155
// 用戶(hù)設(shè)備(POS機(jī), ATM或者柜臺(tái)終端)接口
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(){ // 某個(gè)子類(lèi)實(shí)現(xiàn) }
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. 結(jié)束語(yǔ):

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技術(shù),展示了annotation的class文件格式,并討論了annotation技術(shù)本身的優(yōu)勢(shì)和不足,并于現(xiàn)有的xml技術(shù)加以比較,展望了annotation技術(shù)的應(yīng)用前景AOP。
限于筆者自身的水平(包括技術(shù)水平和寫(xiě)作水平),技術(shù)上對(duì)annotation的學(xué)習(xí)比較有限,寫(xiě)作上也感覺(jué)好多話(huà)無(wú)法用文字來(lái)表達(dá),因而本文的代碼會(huì)比較多(大概有一半以上)。