WEB开发网      濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌涘☉姗堟敾闁告瑥绻橀弻锝夊箣閿濆棭妫勯梺鍝勵儎缁舵岸寮诲☉妯锋婵鐗嗘慨娑氱磽娴e搫鈻堢紒鐘崇墵瀵顓奸崼顐n€囬梻浣告啞閹歌顫濋妸鈺佺闁靛繒濮Σ鍫熺箾閸℃ê濮囨い搴㈡崌濮婃椽宕ㄦ繝鍌氼潓閻庢鍠栭悥濂哥嵁閺嶎厼绠涙い鏃傚亾閿涘繘姊洪崨濠冨瘷闁告洦鍋呴悾顒勬⒒娴e摜鏋冩い顐㈩樀瀹曞綊宕稿Δ鈧粻鏍煃閸濆嫬鏆婇柛瀣崌瀹曠兘顢橀悙鎰╁灪閵囧嫰濡烽敂鍓х杽濠殿喖锕ら幖顐f櫏闂佹悶鍎滈埀顒勫磻閹炬緞鏃堝川椤撶媴绱遍梻浣筋潐瀹曟﹢顢氳椤㈠﹪姊绘担鍛婂暈婵炶绠撳畷褰掑箥椤斿彞绗夊┑鐐村灟閸ㄦ椽鎮¢弴鐔翠簻闁规澘澧庣粙鑽ょ磼閳ь剟宕橀埞澶哥盎闁硅壈鎻槐鏇熸櫏婵犳鍠栭敃銊モ枍閿濆洤鍨濇繛鍡楃箚閺嬪酣鏌熼鍡楀暙椤ユ劙姊婚崒娆戭槮闁硅姤绮嶉幈銊╂偨缁嬭法顦┑鐐叉閸旀帞鎹㈤崱娑欑厽闁靛繆鎳氶崷顓犵焼閻庯綆鍋佹禍婊堟煛瀹ュ啫濮€濠㈣锕㈤弻娑㈡倷椤忓嫬顫囧┑顔硷攻濡炶棄螞閸愵煁褰掑Χ閸℃瑢濮囬梺鐟板槻閹虫﹢鐛幘璇茬鐎广儱鎷嬪Λ婊堟⒒閸屾瑧顦︽繝鈧柆宥呯?闁靛牆顦埀顒€鍟村畷鍗炩槈濡厧骞堥梻浣告贡閸庛倝銆冮崱娑欏亗闁哄洢鍨洪悡娑㈡煕閵夛絽鍔氬┑锛勫帶闇夋繝濠傚閻鏌曢崶褍顏紒鐘崇洴閺佹劙宕ㄩ鐘垫綁闂傚倷绀侀幖顐e緞閸ヮ剙鐒垫い鎺嗗亾缁剧虎鍙冨鎶藉幢濞戞瑥鈧敻鏌ㄥ┑鍡涱€楀褌鍗抽弻锟犲幢濞嗗繆鏋呴梺鍝勭潤閸曨偒鍤ゅ┑鐐叉閸ㄧ敻宕哄畝鍕拺闂傚牊绋掗ˉ鐐烘偨椤栨稑娴柨婵堝仜閳规垹鈧綆鍋勬禍妤呮煙閼圭増褰х紒鎻掋偢閹粙鎳¢妶鍥╋紳婵炶揪缍€椤曟牕鈻撻弴銏$厱闁靛ǹ鍎虫禒銏°亜椤愩垻绠崇紒杈ㄥ笒铻i悹鍥ф▕閳ь剚鎸剧槐鎾存媴閸︻厸妲堝銈嗗灥閹冲酣鍩㈤幘娲绘晣闁绘劏鏅滈弬鈧俊鐐€栧褰掑几婵犳艾绀傛い鎺戝€荤壕濂告煟濡寧鐝€规洖鐭傞弻鏇㈠幢閺囩媭妲銈庡亝缁诲牓鐛崶銊﹀闁稿繐顦伴悗鍛婄節閻㈤潧啸闁轰礁鎲¢幈銊╊敇閵忕姷锛涢梺瑙勫礃缁夘喛銇愰幒鎾存珳闂佹悶鍎弬鍌炲焵椤掆偓閿曨亪寮婚敓鐘茬劦妞ゆ帊鑳堕々鐑芥倵閿濆骸浜為柛妯挎閳规垿鍩ラ崱妤冧画濡炪倖鍨堕悷鈺佺暦閻㈢鍋撻敐搴″幋闁稿鎸鹃幉鎾礋椤掆偓娴犫晠姊虹粙鎸庡攭缂侇噣绠栭幃姗€宕橀瑙f嫼缂傚倷鐒﹂埣銈夘敂閸曢潧娈ㄩ梺鍓插亝濞叉牠鎮块鈧弻锝夊箛椤旇姤姣勭紒鐐劤閵堟悂寮诲☉姘勃闁绘劦鍓涘▓銈夋煛娴e摜澧﹂柟顔筋殘閹叉挳宕熼鍌ゆО缂傚倷绶¢崰鏍崲濡寧顥ら梺璇查叄濞佳囧箺濠婂吘娑㈩敍閻愬鍘靛銈嗙墪濡梻绮堟担鍦浄妞ゆ洍鍋撻柟顔筋殔閳绘捇宕归鐣屼邯闂備胶绮悧婊堝储瑜旈幃楣冩倻閼恒儱浜楅柟鐓庣摠钃辨い顐㈢Т閳规垿鍩ラ崱妤冧户闁荤姭鍋撻柨鏇炲€归崐鐢碘偓瑙勬礀濞层劎澹曟禒瀣厱閻忕偛澧介幊鍛存煕閺傝法校闁靛洤瀚版俊鎼佸Ψ閿旂粯顥i梻浣风串缁插墽鎹㈤崼銉у祦闁哄秲鍔嶆刊瀛樻叏濠靛棙婀伴柟韫嵆濮婄粯鎷呴搹鐟扮濠碘槅鍋勯崯纾嬫"闂佽宕橀褍效閺屻儲鍊甸柨婵嗛閺嬬喖鏌i幘璺烘瀾濞e洤锕俊鍫曞磼濮橆偄顥氶梻鍌欒兌缁垶銆冮崨顓囨稑螖閸涱厾鍘洪梺鍦亾缁剁偤寮崼婵嗙獩濡炪倖妫侀~澶屸偓姘偢濮婃椽鎳¢妶鍛呫垺绻涢懠顒€鈻堥柛鈹惧亾濡炪倖甯掗崯顖炴偟椤忓牊鐓熼煫鍥э工娴滈箖姊婚崒姘偓椋庣矆娓氣偓楠炴牠顢曢敃鈧粻鐘绘煙闁箑骞楅柛娆忕箻閺岀喓绱掗姀鐘崇亶闂佺ǹ顑傞弲鐘诲蓟閿濆围閹艰揪绱曟禒婊勭箾鐎涙ḿ鐭婄紓宥咃躬瀵鎮㈤崗鐓庘偓缁樹繆椤栨繂浜归柣锝嗘そ濮婃椽宕崟顒€娅ょ紓浣筋嚙閻楀棝锝炶箛鎾佹椽顢旈崪浣诡棃婵犵數鍋為崹鍫曟嚌妤e啨鈧倿宕崟銊︽杸闂佸疇妫勫Λ妤佺濠靛鐓熼柣鏂垮级濞呭﹪鏌曢崱鏇狀槮闁宠閰i獮鎺楀籍閸屾稒绶梻鍌欑閹碱偊宕锕€纾瑰┑鐘崇閸庡﹪鏌涢鐘插姕闁抽攱鍨堕幈銊╂偡閻楀牊鎮欓梺璇茬箰瀵墎鎹㈠☉娆愬闁告劖褰冮顐c亜閳哄啫鍘撮柡灞诲姂瀵挳鎮欏ù瀣壕鐟滅増甯楅崑鍌炴煛閸ャ儱鐏柣鎾崇箰閳规垿鎮欓懠顑胯檸闂佸憡鏌i崐婵嬪蓟濞戙垹鐓涢悗锝庡墰钃辨俊鐐€戦崝濠囧磿閻㈢ǹ绠栨繛鍡樻尭缁狙囨煙鐎涙ḿ绠ユ繛鍏肩娣囧﹪濡堕崶顬儵鏌涚€n剙浠遍柡浣稿暣婵偓闁靛牆鍟犻崑鎾存媴缁洘鐎婚梺鍦亾濞兼瑥鈻撻幇鐗堚拺闁告劕寮堕幆鍫熴亜閹存繃鍠橀柣娑卞櫍婵偓闁靛牆妫岄幏濠氭⒑缁嬫寧婀伴柣鐕傚缁﹪鎮ч崼娑楃盎濡炪倖鍔戦崺鍕i幖浣圭厽闁挎繂鎳庡Σ濠氭懚閿濆鐓犳繛鏉戭儐濞呭洭鏌i幘鎰佸剰妞ゎ亜鍟存俊鍫曞幢濮楀棙鈷栭梻浣芥硶閸犲棝宕曢懠顒傜焿鐎广儱鐗勬禍褰掓煙閻戞ɑ灏甸柛妯兼暬濮婅櫣绱掑Ο铏逛桓闁藉啴浜堕幃妯跨疀閿濆懎绠归梻鍥ь槹缁绘繃绻濋崒姘缂備礁顦遍崕銈夊箞閵婏妇绡€闁告侗鍣禒鈺冪磽娴d粙鍝洪悽顖涘笩閻忔帡姊洪幆褏绠婚柍褜鍓氱粙鎺椼€佸鈧濠氬磼濞嗘垵濡介柣搴g懗閸忕姴鎼鍏煎緞婵犲嫭鐓f繝鐢靛仦閸ㄥ墎鍒掓惔銏㈩洸闂侇剙绉甸埛鎺懨归敐鍛暈闁哥喓鍋炵换娑氭嫚瑜忛悾鐢碘偓瑙勬礀缂嶅﹪寮婚崱妤婂悑闁告侗鍨伴獮鍫ユ⒒娴d警鏀伴柟娲讳邯濮婁粙宕熼娑樹簵濠电偛妫欓幐濠氭偂閻樺磭绠鹃柡澶嬪焾閸庢劖绻涢崨顓熷櫣闂囧鏌eΟ铏癸紞闁活厼锕弻宥囨喆閸曨偆浼岄梺鎼炲姂缁犳牠骞冨▎鎾村癄濠㈣泛顦崹婵嬫⒒閸屾瑦绁版い鏇熺墵瀹曟澘螖閸涱偀鍋撻崘顔煎窛闁哄鍨归崣鈧┑鐘灱閸╂牠宕濋弴鐘典笉闁规儼濮ら悡娆撴煙椤栧棗鑻▓鍫曟偡濠婂嫭绶叉繛灞傚妿濡叉劙骞樼拠鑼紲濠电偛妫欓崹鑲╃玻濡ゅ懏鈷戦柛婵勫劚鏍¢梺缁橆殘婵炩偓闁靛棔绶氬浠嬵敇閻愯尙鐛╅梻浣告贡閳峰牓宕㈡禒瀣柧闁挎繂顦伴埛鎴犵磼鐎n厽纭剁紒鐘冲▕閺屾稑螣閻樺弶鍣烘い鎰矙閺岋綁骞囬鍓х槇缂備浇顕уΛ娆撳Φ閸曨垰鍐€闁靛ě鍛帓闂佹眹鍩勯崹杈╃矙閹烘梹宕叉繛鎴欏灩瀹告繃銇勯幘璺烘瀻闁哄濮撮埞鎴︻敊绾嘲濮涚紓渚囧櫘閸ㄥ爼鐛箛娑樺窛閻庢稒锚娴狀參姊绘笟鍥у伎缂佺姵鍨甸埢鎾斥攽閸垻锛濋梺绋挎湰閻燂妇绮婇悧鍫涗簻闁哄洤妫楀ú銈囧瑜版帗鐓曟い顓熷灥濞呮﹢鏌涢妶鍡樼缂佽鲸鎸婚幏鍛嫻椤栨粎绐楃紓鍌欒濡狙囧磻閹剧粯鈷掑ù锝堫潐閸嬬娀鏌涙惔顔肩仸鐎规洘绻傞濂稿川椤忓懐鈧椽姊洪幖鐐插姶闁告挻宀搁崺娑㈠箣閻樼數锛滈柣搴秵閸嬪嫰顢氬⿰鍕瘈闁逞屽墴楠炲秹顢欓崜褝绱查梺璇插嚱缂嶅棝宕戦崨顓犳殾鐎光偓閳ь剟鍩€椤掑喚娼愭繛鎻掔箻瀹曡绂掔€n亞鐣烘繛瀵稿Т椤戝懎顔忓┑鍡忔斀闁绘ɑ褰冮鈺傤殽閻愭惌娈滄慨濠冩そ閹兘寮堕幐搴♀偓顖炴⒑娴兼瑧绉靛ù婊庝簻閻i鎲撮崟顓犵槇濠殿喗锕╅崜娑㈠储閹扮増鈷戦柛婵嗗閸屻劑鏌涢妸锔姐仢闁诡噯绻濇俊鐑芥晜閽樺浼庢繝纰樻閸ㄤ即鎮樺┑瀣亗闁规壆澧楅悡鐔兼煙閹规劖纭鹃柡瀣洴閺岋綁骞欓崘銊ゅ枈閻庤娲栭悥鍏间繆閻戣棄唯闁靛鍎涢幋鐘电=闁稿本鐟чˇ锔姐亜閹存繄澧曢柣锝囧厴閹粙宕归顐g稐闂備礁婀遍崕銈咁潖閼姐倕顥氶柛蹇涙?缁诲棙銇勯弽銊х畵濞存粌缍婇弻锝夋晲閸噥浠╃紓浣介哺閹稿骞忛崨瀛樻優闁荤喐澹嗛鑲╃磽閸屾瑦绁版い鏇嗗洦鍋嬮柛鈩冪⊕閸嬧晝鈧懓瀚伴崑濠傖缚閵娾晜鐓冪憸婊堝礈濮橆厾鈹嶅┑鐘插亞濞兼壆鈧厜鍋撳┑鐘插敪閵娧呯=闁稿本鐟︾粊鏉款渻閺夋垶鎲搁柟骞垮灲瀹曠厧鈹戦幇顓犵▉缂傚倸鍊烽悞锕佹懌婵犳鍨伴顓犳閹烘垟妲堟慨妤€妫楅崜鏉库攽閻愯尙澧涢柛鏃€鐟ラ~蹇撁洪鍕啇闂佺粯鍔栬ぐ鍐€栭崱娑欌拺闁告稑饪村▓鏃堟煕閻旈攱鍋ラ柟顕€绠栭幃婊呯驳鐎n偅娅栭梻浣虹帛閸旀ḿ浜稿▎鎰垫闁搞儺鍓氶埛鎴︽煟閻旂厧浜伴柛銈囧枎閳规垿顢氶埀顒€岣胯閸┿垽骞樺ǎ顒€浜濋梺鍛婂姀閺備線骞忛搹鍦=闁稿本鐟ч崝宥夋嫅闁秵鐓冮梺鍨儏濞搭噣鏌$仦鐣屝㈤柣锝忕節楠炲秹顢欑亸鏍у緧闂佽瀛╅鏍闯椤曗偓瀹曟垶绻濋崒婊勬闂佸湱鍎ら〃鍡涘磹閻戣姤鍊甸柣銏㈡瑜版帞宓侀柛顐犲劜閳锋帒霉閿濆洦鍤€闁崇粯娲熼弻鈩冪瑹閸パ勭彎閻庤娲橀崹鍧楃嵁濡偐纾兼俊顖滃帶楠炴劙姊绘担鍛婂暈濞撴碍顨婂畷鏉款潩鐠鸿櫣鐤囬梺鍛婁緱閸犳洜鎹㈤崱娑欑厱婵炲棗娴氬Σ绋库攽椤斿吋鍠橀柟钘夌埣閺佹劖寰勭€n亙鍝楁繝鐢靛仦閸ㄥ墎鏁幒鎾存珷闁哄被鍎查悡娑㈡煕鐏炵虎娈斿ù婊堢畺濮婂宕掑顑藉亾閻戣姤鍤勯柛鎾茬閸ㄦ繃銇勯弽顐粶缂佲偓婢舵劖鐓涢柛銉㈡櫅閳ь剨缍侀幃銏ゅ传閵壯呮闂備焦鎮堕崕婊堝礃閳轰礁濮冮梻鍌氬€烽懗鍓佸垝椤栫偛钃熼柕濞炬櫆閸庡秵绻濋棃娑卞剰缂備讲鏅犻弻銈夊箒閹烘垵濮屾繛瀛樼矋缁捇寮婚敓鐘茬闁靛⿵绠戦ˇ鈺侇渻閵堝啫鍔氭い锔炬暬瀵鈽夐姀鐘愁棟闁荤姴娲︾粊鎾磻閹炬枼鏀介悗锝庝簽椤旀垿姊洪崜鎻掍簼婵炲弶锕㈠畷鎰版倻閼恒儳鍘介梺鐟邦嚟閸嬪秶绱撳鑸电厱婵せ鍋撳ù婊嗘硾椤繐煤椤忓嫪绱堕梺鍛婃处閸撴瑩宕戝澶嬧拺闁告稑锕ラ悡銉╂煛閸偄澧寸€殿喗鐓″畷濂稿即閵婏附娅栭梻浣虹帛閸旀洟顢氶銏犲偍闁告鍋愰弨浠嬫煟閹邦剙绾фい銉︾矌缁辨帞绱掑Ο铏诡儌缂備緡鍠氱划顖滄崲濠靛棭娼╂い鎾跺Т楠炴劙姊虹拠鑼闁稿鍠栧鏌ヮ敃閿濆棙鐝¢梻浣筋嚙濮橈箓锝炴径濞掓椽鏁冮崒姘憋紱婵犵數濮撮崐濠氬汲閿曞倹鐓熼柡鍐ㄥ€甸幏锟犳煛娴e憡顥㈤柡灞界Х椤т線鏌涢幘瀵告噰闁糕斂鍨归鍏煎緞鐎n偅鐝抽梻浣规偠閸庮噣寮插┑瀣櫖婵犻潧娲ㄧ粻楣冨级閸繂鈷旂紒瀣吹閹叉悂寮堕崹顔芥缂備礁鍊哥粔褰掑箖濞嗘搩鏁嗛柛灞剧矌濡插洭姊绘笟鈧ḿ褎顨ヨ箛鏇炵筏闁告挆鍕幑闂佺粯鍔﹂崗娆愮濠婂牊鐓欓悗娑欋缚缁犳牜鈧懓鎲$换鍕閹烘鏁婇柛鎾楀啰顐奸梻渚€娼ч悧鐐电礊娴e摜鏆︽慨妞诲亾闁糕晪绻濆畷姗€濡搁妷褜鍚嬮梻鍌氬€峰ù鍥敋瑜忛埀顒佺▓閺呮繄鍒掑▎鎾崇闁瑰濮寸粻鐢告煟閻樺厖鑸柛鏂垮缁嬪顓奸崱妯哄伎濠碉紕鍋犻褎绂嶆ィ鍐┾拺闁告繂瀚~锕傛煕閺傝法鐒搁柛鈹垮劜瀵板嫭绻涢姀銏犳瀾鐎垫澘瀚伴幆鍌炲传閵夘灖鎴︽⒑闂堟稒鎼愰悗姘卞娣囧﹪骞栨担瑙勬珳闂佸憡渚楅崢鑹邦杺闂傚倸鍊峰ù鍥敋閺嶎厼绐楁俊銈呮噺閸嬶繝鏌嶉崫鍕櫡闁逞屽厸缁舵艾顕i鈧畷鐓庘攽閸偅效濠碉紕鍋戦崐鏍箰閼姐倖宕查柛鏇ㄥ幘閻棝鏌涢弴銊ョ仭闁抽攱甯¢弻娑氫沪閸撗勫櫗缂備椒鑳舵晶妤呭Φ閸曨垰鍗抽柣鏂挎惈閳峰矂鎮楃憴鍕;闁告鍟块锝嗙鐎e灚鏅濋梺闈涚墕濞村倸危缁嬪簱鏀介柣妯虹仛閺嗏晛鈹戦鑺ュ唉鐎规洘鍔栫换婵嗩潩椤掍浇绶㈤梻浣瑰濞叉牠宕愯ぐ鎺撳亗婵炲棙鍔戞禍婊堟煛瀹ュ骸浜滃ù鐘崇矊闇夋繝濠傛噹椤g厧菐閸パ嶈含闁瑰磭濮甸敍鎰攽閸℃﹩鍞查梻鍌欑閻ゅ洭锝炴径鎰瀭闁秆勵殔閺勩儵鏌涢弴銊ョ仩缂佲偓閸愵喗鐓忓┑鐐戝啯鍣烽柛瀣р偓鏂ユ斀闁挎稑瀚禍濂告煕婵炲灝鈧繂鐣烽姀掳鍋呴柛鎰╁妿椤ρ冣攽閳藉棗鐏熼悹鈧敃鍌氬惞闁哄洢鍨洪崑锝夋煕閵夛絽濡块柕鍫濈摠娣囧﹪骞撻幒鏂库叺闂佸搫鏈ú婵堢不濞戙垹鍗抽柣鎴濇缂嶅矂姊绘担绋挎毐闁圭⒈鍋婇獮濠呯疀濞戞瑥浜楅棅顐㈡处閹尖晠鎮㈤崱娑欏仯濡わ附瀵ч鐘差熆瑜庡ú鐔煎蓟濞戙垹绫嶉柍褜鍓熼獮鎰板箮閽樺鎽曞┑鐐村灟閸ㄧ懓螞濡崵绠鹃柛鈩冪懃娴滄儳螖閺冨倻纾介柛灞剧懄缁佹澘顪冪€涙ɑ鍊愭鐐村姈缁绘繂顫濋鍌ゅ數闂備礁鎲℃笟妤呭垂閹惰姤鍎楁繛鍡樻尭缁犲綊鎮楀☉娅虫垹浜搁鐏荤懓饪伴崼銏㈡毇闂佸搫鏈粙鎴﹀煘閹达箑绀嬫い鎰╁灩琚橀梻鍌欑劍濡炲潡宕㈡禒瀣濡わ絽鍟粻鐔兼煙闂傚鍔嶉柛瀣儔閺屾盯顢曢敐鍥╃暭闂佽崵鍠嗛崝鎴濐潖濞差亜浼犻柛鏇㈡涧閸擃喚绱撴担钘夌厫鐎光偓缁嬫鍤曞┑鐘崇閸嬪嫰鏌i幘铏崳妞わ富鍙冮幃宄扳堪閸愵亞顔婇梺杞扮贰閸犳牠鍩ユ径鎰潊闁挎稑瀚獮鎰版⒒娴e憡鍟炲〒姘殜瀹曟澘螖閸涱厾锛欓梺瑙勫婢ф鎮″☉銏″€堕柣鎰邦杺閸ゆ瑥鈹戦鐓庘偓鍧楀蓟閻旂⒈鏁婇柛婵嗗閸嬫挸鈹戦崱娆愭闂佸湱鍎ら崹鐔肺i崼鐔稿弿婵°倐鍋撴俊顐f⒒濡叉劙鏁撻敓锟� ---闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚敐澶婄闁挎繂鎲涢幘缁樼厱濠电姴鍊归崑銉╂煛鐏炶濮傜€殿噮鍣e畷濂告偄閸涘⿴鍞堕梻鍌欒兌鏋い鎴濇楠炴劙宕滆閸ㄦ繃銇勯幘璺轰汗婵℃彃鐗婃穱濠囶敍濮橆厽鍎撶紓浣哄Ь椤曆囧煘閹达附鍊烽柛娆忣槴閺嬫瑦绻涚€涙ḿ鐭嬬紒顔芥崌楠炲啴鍨鹃弬銉︻潔闂侀潧楠忕槐鏇㈠储閸楃偐鏀介柣鎰綑閻忋儳鈧娲﹂崜鐔兼偘椤斿槈鐔沸ч崶锔剧泿闂備礁鎼崐鍦偓绗涘泚澶愬閳╁啫寮挎繝鐢靛Т閹冲繘顢旈悩缁樼厵闁荤喐婢橀顓炩攽閳╁啯鍊愬┑锛勫厴閺佸倿骞嗚缁嬪牓姊婚崒姘偓鐑芥嚄閸洖绠犻柟鎹愵嚙閸氬綊鏌″搴″箹缂佺媭鍨堕弻銊╂偆閸屾稑顏�
开发学院软件开发Java Apache Geronimo 和 Spring 框架,第 4 部分: 混合... 阅读

Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

 2010-04-16 00:00:00 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨绘い鎺嬪灪閵囧嫰骞囬姣挎捇鏌熸笟鍨妞ゎ偅绮撳畷鍗炍旈埀顒勭嵁婵犲嫮纾介柛灞捐壘閳ь剛鎳撻~婵嬪Ω閳轰胶鐤呯紓浣割儐椤戞瑩宕ョ€n喗鐓曟い鎰靛亝缁舵氨绱撻崘鈺傜婵﹤顭峰畷鎺戔枎閹搭厽袦婵犵數濮崑鎾绘⒑椤掆偓缁夌敻骞嗛悙鍝勭婵烇綆鍓欐俊鑲╃磼閹邦収娈滈柡灞糕偓鎰佸悑閹肩补鈧尙鏁栧┑鐐村灦閹稿摜绮旈悽绋课﹂柛鏇ㄥ灠閸愨偓濡炪倖鍔﹀鈧繛宀婁邯濮婅櫣绱掑Ο璇茶敿闂佺ǹ娴烽弫璇差嚕婵犳碍鏅插璺猴工瀹撳棝姊虹紒妯哄缂佷焦鎸冲畷鎴﹀箻鐠囧弶宓嶅銈嗘尰缁嬫垶绂嶉悙顒佸弿婵☆垳鍘ф禍楣冩倵濮樼偓瀚�闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨奸柟鐧哥秮閺岋綁顢橀悙鎼闂侀潧妫欑敮鎺楋綖濠靛鏅查柛娑卞墮椤ユ艾鈹戞幊閸婃鎱ㄩ悜钘夌;婵炴垟鎳為崶顒佸仺缂佸瀵ч悗顒勬⒑閻熸澘鈷旂紒顕呭灦瀹曟垿骞囬悧鍫㈠幍缂傚倷鐒﹂敋缂佹う鍥ㄧ厓鐟滄粓宕滈敃鍌氱煑闁告劦鐓堝ḿ鏍煕濠靛棗鐝旂憸鏂跨暦閹偊妲炬繛瀵稿Т閵堢ǹ顫忛搹瑙勫珰闁肩⒈鍓涢澶愭⒑閻撳海绉虹紒鐘崇墵楠炲啯銈i崘鈺佲偓濠氭煢濡警妲奸柟鑺ユ礋濮婃椽妫冨☉杈€嗘繝纰樷偓铏枠鐎规洏鍨介幃浠嬪川婵炵偓瀚奸梺鑽ゅ枑閻熴儳鈧氨鍏樺畷顖濈疀濞戞瑧鍘遍梺缁樏壕顓熸櫠閻㈠憡鐓忛柛鈩冾殔閳ь剙婀辩紓鎾寸鐎n亜绐涙繝鐢靛Т鐎氼剟鐛崼銉︹拺缁绢厼鎳庨ˉ宥夋煙濞茶绨芥俊鍙夊姍瀵挳鎮㈤崫鍕ㄥ彏闂傚倸鍊搁崐椋庢濮橆兗缂氱憸宥囧弲闂侀潧鐗嗗ú鐘诲磻閹炬剚娼╂い鎰╁灩缁侇噣姊虹紒妯圭繁闁革綇缍侀悰顕€骞掗幊铏閸┾偓妞ゆ帒鍊绘稉宥夋煥濠靛棙顥犵紒鈾€鍋撻梻鍌氬€搁悧濠勭矙閹达箑姹叉繛鍡楃贩閻熸壋鍫柛顐犲灮閺嗩偊姊洪崫鍕効缂傚秳鐒﹂幈銊╁焵椤掑嫭鐓冮柟顖滃绾偓绻濋埀顒佹綇閵娧呭骄闂佸搫娲ㄩ崰鎾跺姬閳ь剙鈹戦鏂や緵闁告﹢绠栧畷銏ゆ偨閸涘ň鎷虹紓鍌欑劍閿氬┑顕嗙畵閺屾盯骞橀弶鎴濇懙闂佽鍠楄摫婵炵厧绻樻俊鎼佸Χ閸モ晝鏆伴梻鍌欑濠€杈╁垝椤栨粍鏆滈柣鎰摠濞呯姵绻涢幋鐐寸殤缁炬崘鍋愮槐鎾存媴鐠愵垳绱板┑鐐村絻椤曨參鍩€椤掑喚娼愭繛鍙夌墪閻g兘顢楅崘顏冪胺闂傚倷绀侀幉锟犲礉閺囥垹鐤柣妯款嚙缁€鍫熺節闂堟稓澧涚€规洖寮剁换娑㈠箣閻愩劎绱伴梺鍝勬濡鍩為幋锔藉亹閺夊牜鍋勯崢锟犳⒑鏉炴壆鍔嶉柣妤佺矌濡叉劙骞樼€涙ê顎撴繛瀵稿Т椤戝懘骞楅悽鍛娾拺闁革富鍘介崵鈧┑鐐茬湴閸婃繈骞冩ィ鍐╁€婚柦妯侯槺椤斿﹪姊虹憴鍕剹闁告ü绮欏畷鎾绘偨閸涘ň鎷洪梺鑽ゅ枑濠㈡﹢骞冩笟鈧弻锝夊箳閻愮數鏆ら梺璇″枟椤ㄥ﹪鐛弽銊﹀闁稿繐顦扮€氳棄鈹戦悙鑸靛涧缂佹彃娼″畷鏇㈠Χ婢跺﹤鎯為梺閫炲苯澧存慨濠冩そ楠炴牠鎮欏ù瀣壕闁哄稁鍘介崑瀣煟濡灝鍚圭€规挷绶氶悡顐﹀炊閵娧€濮囬梺鍝勬噺閹倿寮婚妸鈺傚亞闁稿本绋戦锟�濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌涘☉姗堟敾闁告瑥绻橀弻锝夊箣閿濆棭妫勯梺鍝勵儎缁舵岸寮诲☉妯锋婵鐗婇弫楣冩⒑閸涘﹦鎳冪紒缁樺灴婵$敻宕熼姘鳖啋闂佸憡顨堥崑鐔哥閼测晝纾藉ù锝呮惈椤庡矂鏌涢妸銉у煟鐎殿喛顕ч埥澶愬閻樼數鏉搁梻鍌氬€搁悧濠勭矙閹烘鍊堕柛顐犲劜閸婄敻鏌i悢鍝勵暭闁哥喓鍋熺槐鎺旀嫚閹绘帗娈绘繝纰夌磿閺佽鐣烽悢纰辨晬婵﹢纭搁崯瀣⒑鐠囨煡鍙勬繛浣冲洤绠烘繝濠傜墛閸嬧晛鈹戦崒姘暈闁抽攱鍨归惀顏堫敇閻愭潙顎涘┑鐐插悑閸旀牜鎹㈠☉銏″殤妞ゆ巻鍋撻柡瀣閵囧嫰顢曢姀銏㈩唺缂備浇椴哥敮鎺曠亽闂佸吋绁撮弲婊堝吹瀹€鍕拻濞撴埃鍋撻柍褜鍓涢崑娑㈡嚐椤栨稒娅犳い鏃囧亹閺嗗棝鏌ㄥ┑鍡欏闁告柨鐏氶妵鍕晜閻e苯寮ㄩ梺璇″櫙缁绘繃淇婇懜闈涚窞閻庯綆鍓欑敮楣冩⒒娴gǹ顥忛柛瀣噽閹广垽宕橀鑲╋紱濡炪倕绻愰幊鎰不閸撗€鍋撻悷鏉款棌闁哥姵娲滈懞杈ㄧ節濮橆剛鐣鹃梺缁樻煥閸氬鍩涢幋锔藉€甸柛锔诲幖鏍¢梺闈涙閸熸挳寮婚妶澶婄闁肩⒈鍓欓悡鐔兼倵鐟欏嫭绀冪紒璇茬墦瀵偊宕橀鑲╁姦濡炪倖甯掔€氀囧焵椤掍焦顥堢€规洘锕㈤、娆撳床婢诡垰娲﹂悡鏇㈡煃閳轰礁鏋ゆ繛鍫燂耿閺岋綁鎮㈢粙鍨潚濠殿喖锕ュ浠嬪箖閳╁啯鍎熼柍鈺佸暞閻︼綁姊绘担铏瑰笡闁绘娲熸俊鍓佺矙鐠恒劍娈鹃梺缁樺灦宀h法寮ч埀顒勬⒑閹肩偛鍔€闁告劑鍔庨妶顕€姊婚崒娆戠獢婵炰匠鍕垫闊洦娲橀~鏇㈡煛閸ャ儱鐏╅柛灞诲妽閵囧嫯绠涢幘璺侯杸闂佹娊鏀遍崹鍧楀蓟閻斿吋鍤冮柍杞版缁爼姊洪崨濠冣拹妞ゃ劌锕濠氭晸閻樻彃绐涘銈嗘閺侇喗鎱ㄩ崶鈺冪=濞达絿枪閳ь剙婀遍弫顕€鎮㈡俊鎾虫川閳ь剟娼ч幗婊呭婵傜ǹ绾ч柛顐g☉婵¤偐绱掑Δ浣侯暡缂佺粯鐩幃鈩冩償閿濆浂鍟嬮梻浣虹《閺備線宕滃┑瀣闁告稑鐡ㄩ悡銉╂倵閿濆懐浠涚紓宥嗩殜濮婂宕掑顑藉亾瀹勬噴褰掑炊瑜滃ù鏍煏婵炵偓娅嗛柛濠傛健閺屻劑寮撮悙娴嬪亾閸濄儳涓嶉柡灞诲劜閻撴洟鏌曟径妯烘灈濠⒀屽櫍閺岋紕鈧絺鏅濈粣鏃堟煛瀹€鈧崰鏍х暦濠婂棭妲鹃柣銏╁灡閻╊垶寮婚敓鐘插窛妞ゆ棁妫勯埀顒佸姍閺岋紕浠︾拠鎻掝潎闂佽鍠撻崐婵嗙暦閹烘垟妲堟慨妤€妫旂槐锟�闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨绘い鎺嬪灪閵囧嫰骞囬姣挎捇鏌熸笟鍨妞ゎ偅绮撳畷鍗炍旈埀顒勭嵁婵犲嫮纾介柛灞捐壘閳ь剛鎳撻~婵嬪Ω閳轰胶鐤呯紓浣割儐椤戞瑩宕ョ€n喗鐓曟い鎰靛亝缁舵氨绱撻崘鈺傜婵﹤顭峰畷鎺戔枎閹搭厽袦婵犵數濮崑鎾绘⒑椤掆偓缁夌敻骞嗛悙鍝勭婵烇綆鍓欐俊鑲╃磼閹邦収娈滈柡灞糕偓鎰佸悑閹肩补鈧尙鏁栧┑鐐村灦閹稿摜绮旈悽绋课﹂柛鏇ㄥ灠閸愨偓濡炪倖鍔﹀鈧繛宀婁邯濮婅櫣绱掑Ο璇茶敿闂佺ǹ娴烽弫璇差嚕婵犳碍鏅插璺猴工瀹撳棝姊虹紒妯哄缂佷焦鎸冲畷鎴﹀箻鐠囧弶宓嶅銈嗘尰缁嬫垶绂嶉悙顒佸弿婵☆垳鍘ф禍楣冩倵濮樼偓瀚�  闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚敐澶婄闁挎繂鎲涢幘缁樼厱闁靛牆鎳庨顓㈡煛鐏炶鈧繂鐣烽锕€唯闁挎棁濮ら惁搴♀攽閻愬樊鍤熷┑顕€娼ч~婵嬪Ω瑜庨~鏇㈡煙閹规劦鍤欑痪鎯у悑缁绘盯宕卞Ο铏圭懆闂佸憡锕槐鏇犳閹惧鐟归柛銉戝嫮褰梻浣规偠閸斿矂鎮ラ崗闂寸箚闁圭虎鍠栫粈鍐┿亜閺冨倸甯剁紒鎰洴濮婃椽宕崟鍨ч梺鎼炲妼缂嶅﹤鐣烽姀鐘嗘椽顢旈崨顓涘亾閸偒娈介柣鎰皺娴犮垽鏌涢弮鈧喊宥夊Φ閸曨垱鏅滈悹鍥皺娴犳悂鎮楃憴鍕┛缂佺粯绻堥悰顔芥償閵婏箑娈熼梺闈涳紡閸愩劌顩梻鍌氬€搁オ鎾磻閸曨個娲晝閳ь剛鍙呴梺鍝勭Р閸斿孩鏅堕敓鐘斥拻闁稿本鐟︾粊鐗堛亜閺囧棗鎳夐崑鎾诲垂椤愩垺璇為悗瑙勬礃缁捇骞冮姀锛勯檮濠㈣泛顑囩粙渚€姊绘担鐟板姢缂佺粯鍔曢敃銏℃綇閳轰緡妫滈梺绋跨箻濡法鎹㈤崱妯镐簻闁哄秲鍔庨。鏌ユ煙椤栨氨澧涢柕鍥у椤㈡洟濮€閵忋垹濮辨繝娈垮枛閿曘劌鈻嶉敐澶婄闁绘ḿ绮崵鎴炪亜閹烘垵浜為柛鐐额潐缁绘繂鈻撻崹顔界亶闁煎灕鍥ㄧ厱闁哄啠鍋撻柛銊ョ仢閻e嘲煤椤忓嫮鍔﹀銈嗗笂闂勫秵绂嶅⿰鍫熺厵闁告繂瀚倴濡炪倧璁g粻鎾诲蓟濞戙垹围閹肩补鈧枼鎷梻浣告惈閺堫剟鎯勯鐐靛祦婵☆垰鍚嬪畷澶愭煟閹寸偍缂氶柍褜鍓濋褔鈥旈崘顔嘉ч幖绮光偓鑼泿缂傚倷鑳剁划顖炲礉濡ゅ懎鐭楅柛娑卞弾濞撳鏌曢崼婵囧櫧缂佺姳鍗抽幃妤€顫濋悡搴♀拫闂佽鍠楅〃濠囥€佸鈧幃婊堝幢濡櫣妲i梻鍌欑窔濞佳囨偋閸℃あ娑樷枎閹寸姷鐒奸梺閫炲苯澧存慨濠冩そ閹兘寮堕幐搴㈢槪闂傚倷绀佹惔婊呭緤娴犲鍋╃€瑰嫭澹嬮崼顏堟煕閺囥劌浜介柛銈冨€濋弻锝嗘償閵忊懇濮囬柤瑁ゅ€濋弻鐔兼煥鐎n偁浠㈠┑顔硷功缁垳绮悢鐓庣倞鐟滃瞼鑺辨禒瀣拺缂佸顑欓崕鎰版煟閳哄﹤鐏︽鐐诧躬閹垻鍠婃潏銊︽珫婵犵數鍋為崹鍫曟偡閿曞倹鍋熺€瑰嫰鍋婂〒濠氭煏閸繃顥為柣鎾卞劚椤儻顦撮柡鍜佸亝缁旂喖寮撮姀鈥充簵闁瑰吋鎯岄崰姘跺船閻㈠憡鐓熼柣妯煎劋椤忕娀鎮樿箛瀣鐎规洘绻堥幃銏ゆ惞閸︻叏绱插┑鐘灱濞夋稓鈧稈鏅犻、鏃堟偐缂佹ḿ鍘撻悷婊勭矒瀹曟粌顫濇0婵囨櫓闂佸搫绋侀崢鑲╃玻濡ゅ懏鐓涚€广儱楠搁獮鎰版煃瑜滈崜銊х不閹捐钃熼柕濞р偓閸嬫捇鏁愰崒娑欑彇缂備胶濮甸懝楣冨煘閹达富鏁婇柣锝呯灱閻g敻姊哄ú璇插箹闁绘妫楀畵鍕⒑缂佹ê鐏卞┑顔哄€濋崺娑㈠箣閻樼數锛滈柣搴秵閸嬫帡宕曢妷鈺傜厱閹兼番鍨规慨宥夋煛瀹€鈧崰鏍箖閻戣姤鍋嬮柛顐ゅ枑閸婄兘姊洪幑鎰惞闁稿鍊濆璇测槈閵忕姷鐤€闂佸疇妗ㄧ粈浣告暜濠电姷顣介崜婵娿亹閸愵喗鍋嬪┑鐘插瀹曞弶绻涢幋娆忕仼妤犵偑鍨烘穱濠囧Χ閸涱厽娈跺銈嗘煥椤︻垶鍩為幋锔藉€烽柤纰卞墮椤も偓闂備焦瀵уú蹇涘垂瑜版帪缍栭煫鍥ㄦ礈绾惧吋淇婇婵愬殭妞ゅ孩鎸绘穱濠囨倷瀹割喖鍓扮紓浣靛姀閸嬫劙骞夋导鏉戝耿婵炲棙鍨归敍婵嬫⒑缁嬫寧婀版慨妯稿姀閳敻姊绘担鍛靛綊顢栭崱娑樼闁煎鍊栧畷鍙夌箾閹寸偛鐒归柛瀣尭閳藉鈻庣€n剛绐楅梻浣规た閸樺綊宕愬┑瀣摕婵炴垯鍨圭粻濠氭偣閾忕懓鍔嬮柣蹇撶墦閹鎲撮崟顒傤槰濠碉紕鍋樼划娆忕暦濞差亜鐒垫い鎺嶉檷娴滄粓鏌熼崫鍕ф俊鎯у槻闇夋繝濠傚閻帡鏌″畝鈧崰鏍箖濠婂吘鐔兼惞闁稒妯婇梻鍌欑窔閳ь剛鍋涢懟顖涙櫠椤斿墽纾界€广儱鎷戝銉︺亜閺囶亞绉鐐叉捣缁數鈧綆鍋呭▍宀勬⒒娴e憡鍟炴繛璇х畵瀹曟粌鈽夐姀鐘插亶闂備緡鍓欑粔鐢告偂閻旂厧绠归柟纰卞幖閻忥絿绱掓径鎰锭闂囧绻濇繝鍌涘櫣闁告ɑ鎸抽弻锝夊箼閸愩劌鈷嬪銈冨灪椤ㄥ﹤鐣烽幒鎳虫棃鍩€椤掍胶顩插Δ锝呭暞閸嬧剝绻涢崱妤冪妞ゅ浚浜為惀顏堝箚瑜滈悡濂告煛鐏炲墽鈽夐摶鏍煃瑜滈崜鐔风暦椤栫偛閿ゆ俊銈咃攻閺咁亪姊洪幐搴g畵妞わ富鍨虫竟鏇°亹閹烘挾鍘介梺鍝勬处濮樸劑宕濆澶嬵棄閻庯綆鍠楅埛鎺楁煕鐏炲墽鎳呮い锔肩畵閺岀喓鍠婇崡鐐扮盎闁捐崵鍋ら幃妤呮濞戞瑦鍠愭繛鎴炴尭缁夊綊寮婚悢鐓庣闁逛即娼у▓顓㈡⒑閽樺鏆熼柛鐘崇墵瀵寮撮悢铏诡啎闂佺粯鍔﹂崜姘舵偟閺囥垺鈷戠紒瀣儥閸庡繑淇婇锝囩疄鐎殿喛顕ч埥澶婎潩椤愶絽濯伴梻浣告啞閹稿棝鍩€椤掆偓鍗遍柛顐g箥濞撳鏌曢崼婵囧殗闁绘稒绮撻弻鐔煎礄閵堝棗顏�
核心提示:开始之前本系列教程适合于需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 应用服务器上使用 Spring 框架的强大功能的 Java™ EE 开发人员,关于本系列教程本系列教程共分为 6 个部分,Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用

开始之前

本系列教程适合于需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 应用服务器上使用 Spring 框架的强大功能的 Java™ EE 开发人员。

关于本系列教程

本系列教程共分为 6 个部分,向您介绍了 Spring 框架及 Spring 框架怎样与 Geronimo 结合使用。我们将从检验各种 Spring 框架方法及其怎样与 Geronimo 服务器结合使用入手。在整个系列教程中,您将开发和部署个人电话本应用程序。该应用程序包括以下功能:

显示电话本

显示每个条目的详细信息

向电话本中添加一个新条目

编辑、修改和删除条目

向条目中添加更多详细信息,例如主电子邮件地址

第 1 部分 介绍了 Spring 框架的各个模块,并介绍了每个模块与在 Geronimo 应用服务器上开发 Java EE 应用程序的关系。该部分还说明了 Spring 框架所基于的方法。

第 2 部分 介绍了如何使用 Spring 框架在 Geronimo 上构建第一个骨架系统应用程序。

在 第 3 部分 中,您将采用通过 Apache Derby 数据库添加 Java 数据库连接 (JDBC™) 支持来扩展在第 2 部分中获得的 Geronimo 应用程序。您还将了解如何将对象关系映射集成到使用 iBatis 的应用程序中。

在第 4 部分中,您将面对 Spring 面向方面编程 (AOP) 和 Spring Web 框架。使用 Spring AOP,任何受 Spring 框架管理的对象都可变为面向方面的,并且本教程利用了通过 Spring AOP 提供的声明式事务管理服务。

第 5 部分介绍了 Spring Model-View-Controller (MVC)。该教程向您介绍了 Spring MVC 框架及 Web 视图,使您可以了解 Spring MVC 的入门知识。

在本教程结束的第 6 部分,介绍了如何通过 Spring 框架使用 JavaServer Page™(JSP™)、Velocity、Tile 和 PDF 导出功能。您将使用和体验 Spring MVC 内置的各种 Web 视图。

关于本教程

在 第 3 部分 中,扩展了电话本应用程序,使其从 Derby 数据中动态读取数据;并且您看到了动态更改数据源而不碰一行代码是多么简单。使用了 Spring 框架提供的 DAO 和 ORM 模块来扩展应用程序,并且还了解了如何将其部署到 Geronimo 上。

在本教程中,将通过引入事务把应用程序带入到下一个级别,事务是一组可恢复的动作,其保证的输出要么是采取了所有动作,要么是未采取任何动作(如 “漫游 Web 服务原子事务操作” [developerWorks,2004 年 9 月] 文章所述)。Spring AOP 模块是随 Spring 框架分发预先打包而来的,并且事实上它也是核心模块之一。您将看到 Spring 如何支持方面、连接点、建议和切入点等 AOP 概念,然后再将把这些概念引入应用程序中。您将向应用程序中添加一个新页面以添加新条目,从而把事务引入到应用程序中。

您还将了解如何使用 SWF 模块明确定义应用程序的流程。随着应用程序复杂度的增加,管理页面的浏览流程就变得更难,而 SWF 能够缓解这种痛苦。

先决条件

本教程假定您熟悉面向对象的编程 (OOP) 并且熟知 J2EE 和 Java EE 术语。了解面向方面编程更佳,但不作硬性要求。但是,您必须熟悉 AOP 基本概念并了解基本的 XML 语义。本教程涉及到了一些 AOP 概念并会尝试简要加以介绍,但您的主要目的是要了解 Spring AOP。

系统要求

您的系统需要至少满足以下要求才能继续学习本系列教程

The Spring Framework, Version 1.2.8 —— 具有所有依赖性的压缩文件。

Apache Geronimo 1.1 —— Geronimo 是 Apache 的 J2EE 认证应用服务器。

Spring Web Flow —— SWF 是 Spring 框架的一个独立模块,并且需要单独下载。本教程使用版本 1.0-rc3。

Apache Derby 数据库 —— 本教程使用 Derby,该数据库是开源的轻量级数据库。Derby 是嵌入到 Geronimo 1.1 里的,因此不需要再单独安装。

Apache Ant —— 确保正确配置 Ant 并且其 /bin 目录位于 Path 系统变量中。

Java 1.4.2 —— 确保 Java 安装并运行在系统中。

以下是安装和配置开发、部署和运行示例应用程序所需的软件的指导信息。

Spring 框架和 Geronimo 安装 —— 为了使样例代码能够运行,需要安装运行 Geronimo 和 Spring 框架。有关安装指南,请参阅 第 2 部分。

Spring Web Flow —— SWF 模块是一个独立的 API。您将使用版本 1 的发行测试版 3(SWF 版本 1-rc3)。将需要使用此次下载附带的 JAR 文件。下载名为 spring-webflow-1.0-rc3.zip 的压缩文件,并将其保存到硬盘某个位置。下载完后,将压缩文件解压缩到应用程序所在的驱动器中(例如,我把它安装到了驱动器 K: 中)。解压缩该文件应当会创建一个名为 spring-webflow-1.0-rc3 的目录(在我的驱动器中,SWF 的安装目录是 K:\spring-webflow-1.0-rc3)。此时,您不需要为 SWF 做任何配置。

Derby 数据库 —— Geronimo 1.1 预打包附带的 Derby 数据库安装不需要任何特殊配置。您将使用在 第 2 部分 中创建的表,因此请参考该教程以获得创建及填充本教程所需的数据库表的步骤。

Spring AOP 和 Spring Web Flow 简介

在此部分中,将从更深层次向您介绍 AOP、Spring AOP 模块和 Spring SWF 模块。

AOP 的基础知识

AOP 是 Spring 框架的主要组件之一,它基于把横切性对象分离为关注点或方面的原理。如 Graham O'Regan 在其文章 “Introduction to Aspect-Oriented Programming”是这样解释的:“面向方面编程 (AOP) 是面向对象的 (OO) 编程的补充,它允许开发人员动态地修改静态 OO 模型以创建一个可以不断成长以满足新需求的系统。就像现实世界中的对象可以在其生命周期内更改状态一样,应用程序也可以随着开发过程的深入而采用新特性。”

几乎所有现实的软件系统都有若干个关注点,这些关注点横跨了多个模块。实现这些关注点的面向对象的技术并不总是能提供最理想且清晰的解决方案。AOP 方法可以减轻这种痛苦,它允许将此类关注点设计为方面。横切性关注点的最简单示例是日志记录和事务。这两个关注点可以跨越多个模块并且使用 OO 设计方法,您可能会因为要为每个模块定义事务管理而感到有些麻烦,也可能会在使用单个事务模块时遇到同步问题。通过使用 AOP 方法,您可以将其定义为方面,这些方面可以被任何需要使用该方面的模块共享,并且不需要担心管理或事务问题。

下面是 AOP 的一些核心概念:

方面 (Aspect):方面是可影响其核心关注点的编程模块的一部分。它类似于 OO 术语中的类。日志记录和事务都是 J2EE 和 Java EE 应用程序中的横切性关注点的示例。

联结点 (Join point):定义为程序执行时主程序和方面相遇的任何逻辑点。此类联结点的示例包括方法调用和异常。在 Spring AOP 中,一个联结点总是一次方法调用。

建议 (Advice):在 AOP 术语中,建议被定义为一项应当在特殊联结点上采取的操作。这些操作可以是方法调用、过程或抛出异常。在 AOP 中有各种建议,其中 before、after 和 around 是三个主要类型。

切入点 (Pointcut):指定建议何时应当触发的联结点集合。Spring AOP 允许将切入点定义为正则表达式。

目标对象 (Target object):被建议的对象并且包含联结点。

AOP 代理 (AOP proxy):由 AOP 框架创建的对象,用于实现 AOP 关注点,包括建议。在 Spring AOP 中,这可以是 JDK 动态代理或 cglib 代理。

织入 (Weaving):定义为聚合所有方面以创建被建议的对象。Spring AOP 在运行时执行织入。

不同的建议类型包括:

Around 建议:这是围绕联结点的一种建议。在 AOP 中,联结点通常是方法调用。它可以控制程序的执行,并且可以定义逻辑判断执行是应当继续执行,还是应当采取另一条路径。这种控制功能使得此建议被广泛应用到了 AOP 应用程序中。

Before 建议:这是在联结点之前执行的建议。但是,它没有像 Around 建议一样拥有的对程序流程的控制。

After 建议:类似于 Before 建议,只不过在这种情况下是在联结点正常执行完后才执行该建议。这种建议也不能像 Around 建议一样可以控制流程。

Throws 建议:Spring AOP 允许在方法(联结点)抛出异常时定义此类特殊建议。

Spring AOP

上一部分介绍了几种基本的 AOP 术语。现在,让我们来看看 Spring AOP 中的等效术语。Spring 框架目前支持 AOP 的概念集,范围从切入点到建议。

使用 Spring 作为建议程序或拦截程序来实现方面。

在 Spring AOP 中,联结点始终是方法调用。

Spring AOP 为作为拦截程序对象的所有 AOP 建议提供了实现。

Spring AOP 支持使用正则表达式声明性地定义切入点。

在 Spring 中,AOP 代理将是 JDK 动态代理或 cglib 代理。

图 1 显示了 Spring AOP 如何支持此处定义的概念。

图 1. Spring AOP 中的 AOP 概念
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

使用示例电话本应用程序,您将看到如何使用 Spring AOP 来定义建议、切入点和建议程序等 AOP 概念。示例还将使用依赖性插入,向您展示如何将这些方面动态插入对象中。

然后介绍由 Spring 提供的事务管理。事务管理将在内部使用 Spring AOP 以实现其目标。最后将扩展电话本应用程序声明性地使用此事务。

Spring Web Flow

根据 Spring 框架的官方文档,“SWF 是专注于在 Web 应用程序内定义用户界面流程的产品”。换言之,SWF 将以一种高度可配置且可重用的方式定义此流程。系统允许捕捉独立工作流程并在各种环境下加以重用。

用 SWF 定义工作流程

下面是使用当前技术定义工作流程会遇到的一些问题:

当前技术处理小型且较简单的导航工作流程很轻松,但随着 Web 应用程序变得越来越大越来越复杂,流程管理开始四分五裂。

不能在各种环境中轻松地重用工作流程,因为流程都是写死的。

Spring SWF 通过允许在配置文件中声明性地定义工作流程来尝试解决这些问题。这样您就可以在各种场景中使用这些工作流程。当 Web 应用程序具有一个像向导一样的流程时,这最有帮助。

在本教程的稍后部分,将使用 SWF 声明性地定义电话本应用程序的流程。还要把在 AOP 部分中定义的方面与 SWF 整合到一起来演示 SWF 的完整功能。

用 Spring AOP 扩展电话本应用程序

在此部分中,将讨论到如何将 Spring AOP 与一个方法标记的方面结合使用。应用程序将使用建议,并会给出定义为方法标记程序或跟踪程序的切入点。还会介绍建议程序中调用了哪些方法以及每个建议程序类中的执行流程。稍后事务也将被引入到此应用程序中,您将看到如何声明性地定义应用程序的流程。所有工作都将在 XML 文件中完成。

这两个版本的应用程序都将使用在 第 3 部分 中定义的 Spring JDBC 类以供数据库调用。

工作区的目录结构

图 2 展示了应用程序的布局。下载本教程附带的源压缩文件并将其解压缩到硬盘驱动器中。

本教程涉及到了此处所示的所有目录及文件。

图 2. 解压缩源文件后得到的应用程序目录结构
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

定义 Add New Entry 页面

需要做的第一件事是为应用程序定义 Add New Entry 页面。该页面将在一张表单中包含构成电话本条目的所有字段。此表单上的提交操作将把一个新条目添加到电话本数据库中。

完成此操作后,将在应用程序中引入和定义方法标记的方面。如先前所述,方法标记程序方面可被视为方法跟踪的方面。基本想法是通过在每个联结点处放置有意义的消息来理解应用程序中的 Spring AOP 执行流程。

将所支持的类用于 Spring AOP 支持的建议是十分直观的。了解方面的一种方法是在应用程序中定义方法标记的方面。虽然它的功能不是很多,但却展示了 AOP 的强大。

下一步将是把另一个方面 —— 事务 —— 引入应用程序中。Spring 将通过允许您在应用程序中声明性地定义此类事务,再次展示其威力和简单性。您不需要更改任何代码。只需对此事务声明性地循环执行数据库调用,Spring 将在成功时把事务提交到数据库,或在满足任意一个已定义的失败条件时将事务回滚。

改进当前的电话本应用程序

图 3 展示了 Geronimo 中的事件流程,第 3 部分 中所述。

图 3. 如何使对 home-aop.jsp 的请求在 Geronimo 应用服务器上得到处理
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

现在来看一看 Spring AOP 支持的三种不同的建议。

事件的 Before 建议流程

此处要为标记方面定义一个用于实现 MethodBeforeAdvice 的类。MethodBeforeAdvice 是 Spring AOP 提供用于实现 AOP 的 Before 建议结构的一个接口类。需要实现此类的 before() 方法,并且 Spring AOP 框架将确保在实际的方法调用前在运行时先调用此方法。下一部分中的顺序图可以更清晰地说明此问题。

另外,还将在 ApplicationContext.xml 文件中引入 Bean 以将此建议应用到目标对象 PhonebookDataProvider 中。此 Bean 还将为 Spring AOP 定义切入点以将此建议应用到目标对象的所有方法中。

Spring 使用代理模式为基于 ApplicationContext.xml 文件中所做的配置的应用程序提供 AOP 工具。

图 4 概括了执行 Before 建议时 Spring 容器里的事件序列。

图 4. 定义 Before 建议后的事件序列
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

如您在 图 4 的序列图中所见,SpringAOProxy 是 ProxyFactoryBean 的一个实例,它负责在切入点处截取执行,然后将特定的建议应用到目标对象上。在这种情况下,首先将在 Spring AOP 的 BeforeAdvice 类的实现中调用 before() 方面。然后通过在 PhonebookAdapter 类中调用实际的 getPhonebookEntries() 方法来结束。

事件的 After 建议流程

类似于 Before 建议,此处将定义一个可以实现 Spring AOP 提供的 AfterReturningAdvice 接口的类,以实现 AOP 的 After 建议结构。

图 5 中描绘的事件流程与 Before 建议的基本一样,惟一的不同是目标对象中的实际方法是在应用 After 建议之前调用的。

换言之,先调用 getPhonebookEntries() 方法,然后 SpringAOProxy 调用 afterReturning() 方法。

图 5. After 建议的事件序列
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

事件的 Around 建议流程

这是 AOP 中最有趣且使用最为广泛的建议。此建议与其他两种建议之间的主要差别在于在实现此建议时可以实施更多控制,因为执行流程将停在由 Advice 类实现的 invoke() 方法中。这就是此建议被称为 主动 建议,而其他建议被称为 被动 建议的原因。

Around 建议专门用于在调用前后实现自定义行为。它们负责选择是返回自己的返回值继续执行操作,还是抛出异常切换到其他执行路径。

Around 建议在 Spring AOP 中被定义为拦截程序。拦截程序是拦截方法并抛出异常以取得程序执行控制的类。图 6 展示了如何为应用程序实现 Around 建议。

图 6. Around 建议的事件执行
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

从图中可以看到,当 Spring 看到一个 Around 建议时,它将在 MethodInterceptor 接口(实现此 AOP 结构的接口)的实现上调用 invoke() 方法。只需注意必须在 MethodInvocation 对象上显式调用 proceed() 方法,以在目标对象上执行该方法。

如果对应用程序开发进行更深层次的钻研,以上定义的所有流程将会更有意义。

插入 Spring AOP 支持的事务管理

应用程序的下一个逻辑步骤是将事务管理引入其中。

声明性事务管理特性是由 Spring 框架中的 Spring AOP 启用的。虽然此特性使用 AOP,但它非常明晰以至于用户不需要了解 AOP,并且无需更改任何代码就可以将事务引入到应用程序中。

这里将使用 TransactionProxyFactoryBean 来创建事务性代理以将事务方面引入应用程序。此 Bean 只是 Spring 的一般 ProxyFactoryBean 的一特例。此 Bean 还将自动创建 TransactionInterceptor 并将其与该代理对象连接起来。

然后,将在必须应用此事务方面的方法上声明性地定义切入点。该定义有三个必须指定的主属性:

transactionManager:要使用的 PlatformTransactionManager 实现(例如,DataSourceTransactionManager 实例)

target:应为其创建事务代理的目标对象

transactionAttributes:每个目标方法名称(或方法名称模式)的事务属性(例如,传播行为和 readOnly 标志)

应用程序将使用 Spring 框架附带的 JDBC DataSourceTransactionManager。target 是服务对象 PhonebookDataProvider 的接口定义。此处将为应用程序的 add 方法定义事务属性。

事务性代理将实现目标的接口;在本例中,为 PhonebookDataProvider 类。这是为此类定义接口的主要原因。

应用程序的数据模型定义和数据库设置

在尝试访问数据库前,必须确保数据库已正确设置。这里将使用在第 3 部分中创建的同一个数据库。因此如果遵循本系列教程的 第 3 部分 中的指导信息进行了操作,则应该已全部设定完毕;如果没有遵循上述指导信息,则必须返回并完成第 3 部分,然后才能继续本教程。

准备好数据库中的数据后,可以继续进行应用程序处理。根据先前部分中的建议,需要先定义 Add New Entry 页面。然后从最简单的方面 —— 方法标记(或方法跟踪)—— 开始,接下来把事务声明性地添加到应用程序中。稍后您将看到如何使用 SWF 声明性地定义相同的导航流程。

定义 Add New Entry 页面

需要使用此页面将导航流程添加到应用程序中以及显示 Spring 框架的事务管理特性。此页面的 JSP 非常简单,只有一张表单和几个动作。您可以在 <WORKSPACE>/ phonebook\web\WEB-INF/jsp 目录中查看这段代码。图 7 显示了运行此页面在浏览器中会看到的一个结果画面。当您在应用程序主页中单击 Add an Entry 按钮时将调用此页面。

图 7. 应用程序的 Add New Entry 页面
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

此时,所需的应用程序的视图部分就准备好了。下一步要做的是引入方面。

引入 AOP 方面

在此部分中,将定义接口并将涉及 Before 建议、After 建议和 Around 建议类。然后将构建、部署并在 Geronimo 中运行电话本应用程序。

在电话本中实现跟踪方面

用于定义方面和事务管理的 AOP 代理将实现目标对象的接口:PhonebookDataProvider 。因此就需要为服务类定义接口。

回想一下在 第 3 部分 中定义的 PhonebookDataProvider 类。该类用作客户机使用的服务类,并且完成了制作数据库连接和将结果返回给客户机的所有苦活。该类由 JSP 实例化以从应用程序中获取数据。将同样使用这个类引入跟踪方面,但由 Spring AOP 与接口协作。因此需要完成的第一项工作是为此类编写接口。

清单 1 显示了 IPhonebookDataProvider 接口。该接口中包含了将要开发的应用程序部分所需的所有方法。JSP 将调用 getEntries()。







清单 1. 应用程序的 PhonebookDataProvider 接口

/** 
 * IPhonebookDataProvider.java 
 * This class defines the methods to be used by the PhonebookDataProvider class. 
 * 
 * @author Arun Chhatpar 
 */ 
public interface IPhonebookDataProvider { 
  public List getPhonebookEntries() throws Exception; 
 
  public PhonebookEntry getPhonebookEntry(int entryID, int rowID) throws Exception; 
 
  public int addEntry(PhonebookEntry pe) throws Exception; 
} 

此类没有多少代码,只有方法定义。有了接口之后,就可以让服务类来实现该接口。

PhonebookDataProvider 实现新接口

PhonebookDataProvider 将实现新定义的接口。此处的大部分函数与在 第 3 部分 中为应用程序定义的函数相同。请注意,有一个新方法用于支持 Add new PhonebookEntry 功能。

清单 2. PhonebookDataProvider 定义所有方法并实现 IPhonebookDataProvider 接口

public class PhonebookDataProvider implements IPhonebookDataProvider{ 
  private IPhonebookDAO pbDao; 
 
  public PhonebookDataProvider() { 
  } 
 
  public void setPbDao(IPhonebookDAO pbDao) { 
    this.pbDao = pbDao; 
  } 
 
  public List getPhonebookEntries() throws Exception{ 
    return pbDao.getPhonebookEntries(); 
  } 
 
  public PhonebookEntry getPhonebookEntry(int entryID, int rowID) throws Exception{ 
    return pbDao.getPhonebookEntry(entryID, rowID); 
  } 
 
  public int addEntry(PhonebookEntry pe) throws Exception{ 
    return pbDao.addEntry(pe); 
  } 
} 

引入方法标记的方面

方法标记的方面将捕捉所有对被标记方法的调用及从其返回的结果。我们将在这些联结点处显示一条消息。如在 AOP 的基础知识 部分中的定义,有三类建议:Before、After 和 Around。Spring AOP 有支持全部三类建议的接口。我们将定义三个类用于实现这些接口并在每个接口中放置标记程序以查看运行情况。

Before 建议

MarkingBeforeAdvice 类用于为本 AOP 示例定义 Before 建议。它将实现 MethodBeforeAdvice 接口。下面是从该接口实现 before(...) 方法的代码。

清单 3. 实现 Before 建议的示例

public class MarkingBeforeAdvice implements MethodBeforeAdvice 
{ 
  public void before(java.lang.reflect.Method method, Object[] obj, Object obj2) 
throws Throwable { 
    Calendar cal = Calendar.getInstance(); 
    System.out.println(cal.getTime() + " :::: PREPARING TO CALL "+method.getName()+" 
of "+obj2.getClass().getName()+""); 
  } 
 
} 

这个 Advice 类定义用于指定实现 before() 方法以启用 Spring 运行时在达到适当的联结点时通报建议。

必须让 Spring 应用程序上下文识别到此建议,还要将此建议附加到应用程序的适当联结点上。清单 4 显示了实现上述操作的配置。

清单 4. 向应用程序上下文中添加 Before 建议

  <bean id="beforeTracer" 
class="phonebook.aop.MarkingBeforeAdvice"/> 
     
  <!-- Bean Definitions for Advisors --> 
     
  <!-- Before Marking Advisor --> 
  <bean id="beforeMarkingAdvisor" 
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
    <property name="advice"> 
      <ref local="beforeTracer"/> 
    </property> 
    <property name="pattern"> 
      <value>.*</value> 
    </property> 
  </bean> 
         
  <bean id="phonebook" 
class="org.springframework.aop.framework.ProxyFactoryBean"> 
    <property name="proxyInterfaces"> 
      <value>phonebook.dao.IPhonebookDataProvider</value> 
    </property> 
    <property name="target"> 
      <ref local=" phonebookJdbcDataProvider"/> 
    </property> 
    <property name="interceptorNames"> 
      <list> 
        <value>beforeMarkingAdvisor</value> 
      </list> 
    </property> 
  </bean> 

此处的第一个 Bean 定义用 Before 建议接口的实现定义一个新 Bean。

第二个定义将定义 before 标记建议程序。它定义了切入点及必须在每个切入点上执行的建议。可以使用正则表达式为应当应用建议的选定方法设定模式。我仅使用了 .* RE 将此应用到目标对象的所有方法中。

使用 ProxyFactoryBean 的最后一个 Bean 是起整合作用的实际 Bean。ProxyFactory 在接口上运行,并且它将仅把此方面应用到实现在此属性中定义的接口的类中。目标用于指定方面应当围绕的对象。

After 建议

After 建议的定义与上述类似;开始先定义一个用于实现 AfterReturningAdvice 接口的类。afterReturning() 方法将在目标对象上的实际方法执行完后在运行时被拦截。

可以在 src 目录中查看类和定义。

Around 建议

添加 Around 建议的过程与上面说明的 Before 建议定义十分相似,只有一处重要区别。由于 Around 建议在 Spring AOP 中是作为拦截程序被实现的,因此实现类将实现 MethodIntercepter 接口。

另一个重要区别在于通过在 MethodInvocation 对象上调用 proceed() 方法会明显延长执行流程。如果不这样做,则 Around 建议方面将不在目标对象上调用方法。这种设计为开发人员提供了定义实际方法被调用后的前操作和后操作的能力。

清单 5 展示了 Around 建议类的定义。

清单 5. 实现 Around 建议的类

public class MarkingInterceptor implements MethodInterceptor { 
  public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) 
throws Throwable { 
 
    Calendar cal = Calendar.getInstance(); 
    System.out.println(cal.getTime() + " :::: NOW INVOKING 
"+methodInvocation.getMethod().getName()+"."); 
 
    Object retVal = methodInvocation.proceed(); 
 
    cal = Calendar.getInstance(); 
    System.out.println(cal.getTime() + " :::: FINISHED INVOKING 
"+methodInvocation.getMethod().getName()+"."); 
 
    return retVal; 
  } 
} 

您可以看到此类与压缩文件附带的可部署的 .war 文件协作,也可以按照以下说明构建和部署该文件。

构建、部署和运行

本教程附带的源压缩文件配有所有类、配置文件和 Ant 构建文件(如果需要构建)。压缩文件中还有一个配有全部所需内容的可部署的 .war 文件。可以使用任何一种方法来获取 phonebook.war 文件。

还必须确保 <WORKSPACE>/phonebook/lib 目录中包含 readme.txt 文件中提及的所有 JAR 文件。请仔细阅读 readme.txt 文件中的说明并确保将所有必需的文件都复制到 <WORKSPACE>/phonebook/lib 中。

注: 可以参考本系列教程的 第 2 部分 中的构建和打包说明。

使用 Geronimo 中的 Deploy New 工具部署 phonebook.war。如果一切运行正常,将在 Geronimo Web Console 上看到一条消息,说明 Phonebook application deployed successfully。

现在,将浏览器指向新页面:http://localhost:8080/phonebook/home-aop.jsp。

如果一切都按预期运行,主页应当如 图 8 所示。

图 8. 运行在应用服务器中的 home-aop.jsp
Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow

应当会在 Geronimo Console 上看到系统消息,说明正被执行的所有建议。

刚刚看过了在应用程序中运行的跟踪方面。现在来看看如何通过在应用程序上下文文件中添加几个 Bean 轻松地声明性地引入事务管理方面。

用几个 Bean 添加事务

是的,这就是将事务方面插入应用程序所需完成的全部工作。请参阅 清单 6 中的这些 Bean。

清单 6. 事务管理 Bean

 <!-- Bean Definitions required for Transactional Support --> 
     
  <bean id="txManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="pbJdbcDataSource"/> 
  </bean> 
     
  <bean id="phonebookTxn" 
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
> 
    <property name="transactionManager" ref="txManager"/> 
    <property 
name="proxyInterfaces"><value>phonebook.dao.IPhonebookDataProvider</ 
value></property> 
    <property name="target" ref="phonebookJdbcDataProvider"/> 
    <property name="transactionAttributes"> 
      <props> 
        <prop key="add*">PROPAGATION_REQUIRED,-Exception</prop> 
      </props> 
    </property> 
  </bean> 

第一个 Bean 用于定义管理事务时必须使用的事务管理程序。由于需要包装 JDBC 事务,因此使用 Spring JDBC 模块中的 TransactionManager 实现。它配有应用建议所需的方法拦截程序。

在本教程的先前部分中讲解过 TransactionProxyFactoryBean。现在是时候来查看一下它是如何实现事务所需的核心功能的。并且如您所见,您可以向它插入任何事务管理程序并使其相互协作。需要定义一个目标对象,在这个目标对象上事务应该被应用到目标属性内。将使用在 第 3 部分 中定义的 phonebookJdbcDataProvider Bean 实现此操作。在那里还可以学到定义此 Bean 和其他 Bean 向 Derby 数据库读取和写入数据。

最后而且也是最重要的一步,定义 transactionAttributes。事务属性包含需要使用哪些事务语法的定义。此处就是 把事务应用到以 add 为开头的所有方法中。PROPAGATION_REQUIRED 表示支持当前的事务或创建一个新事务(如果没有)。那一行中的最后一个条件告诉事务管理程序在出现异常时回滚事务。减号表示回滚;如果在出现异常时仍要提交,则使用加号(+)。

这就是将事务引入应用程序所需完成的全部工作。

构建并运行

全部完毕。运行 Ant 来构建并创建 WAR 文件。使用 Geronimo 中的 Deploy New 工具部署该文件。

让浏览器指向新页面:http://localhost:8080/phonebook/home-aop.jsp。

将获得同样一个主页,但这一次它是围绕事务的。应当亲自尝试添加并查看新条目。下面是一些用于测试事务管理程序的提示:

在 Add New Entry 页面中添加所有字段;应当会看到所有信息都返回到主页列表中。

不输入名字或姓氏。将会看到一条错误消息,并且在后端,事务会被回滚。可以通过查看主页或数据表本身加以验证。

最后也是最重要的一个测试是添加名字和姓氏并将其他所有字段留空。将会再次看到错误消息,事务也将会被回滚。但如果还记得数据库表定义和 AddEntry() 方法的 SQL 语句,就可以使用它们把数据插入第一张表中 —— FirstName 和 LastName,然后把其余数据添加到第二张表中。有两次插入操作,因此如果在第二次插入时出现异常,事务管理程序将回滚最后一次提交的 SQL 语句。

至此 AOP 就全部介绍完了。完成了向应用程序中定义并添加两个方面,现在将要添加 SWF。

Spring Web Flow

SWF 系统允许捕捉 UI 流程作为独立模块。这些模块可在各种系统中重用。这里将改用 SWF 来定义应用程序的导航流程。

在 SWF 中,每个流程都是一个有限状态机 (FSM),其中包含状态和转换。状态表示执行一些动作或显示给用户以供用户输入的视图。开始先定义 phonebookFlow.xml 文件。此文件将包含流程定义。

注:在流程定义中将不定义动作;那是高级主题。

然后将对 JSP 表单做一些小改动,并查看其运行情况。

添加 SWF

现在可以将静态流程和硬编码流程改为使用 SWF 声明性地定义。可以通过三个步骤来实现:

在 XML 文件中定义应用程序的 Web 流程。

定义 XMLFlowFactory Bean 在 ApplicationContext 中访问此流程。

执行此流程。

第一步是在 XML 文件中定义流程。清单 7 显示了用于本应用程序的这一流程。



清单 7. PhonebookFlow.xml 为应用程序定义流程

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE flow PUBLIC "-//SPRING//DTD WEBFLOW 1.0//EN" 
    "http://www.springframework.org/dtd/spring-webflow-1.0.dtd"> 
 
<flow start-state="showPhonebook">   
   
  <!-No Action Processing plugged in so far. Just getting the flow in place first  
--> 
   
  <view-state id="showPhonebook" view="home-dao"> 
    <transition on="goToAddEntry" to="addEntry">       
    </transition> 
  </view-state> 
   
  <view-state id="addEntry" view="addentry"> 
    <transition on="goToHome" 
to="showPhonebook"></transition> 
    <transition on="saveEntry" to="addEntry"></transition> 
  </view-state> 
</flow> 

此配置文件是不言自明的。此 XML 中的顶部条目用于为应用程序定义启动状态。然后定义 showPhonebook 状态的视图状态,然后再去定义 addEntry 状态。视图属性定义用于保留特殊视图定义的 JSP。

流程部署

在 清单 7 中,看到了定义流程是多么容易。现在来看一看如何在特殊环境(例如 Spring MVC)内部署该流程(参见 清单 8)。

清单 8. 部署此流程所需的配置

<bean id="flowRegistry" 
class="org.springframework.webflow.registry.XmlFlowRegistryFactoryBean"> 
    <property name="flowLocations" value="/WEB-INF/phonebookFlow.xml"/> 
  </bean> 
 
  <!-- Creates, saves, and restores managed flow executions --> 
      <bean id="repositoryFactory" 
class="org.springframework.webflow.execution.repository.continuation.ContinuationFlow 
ExecutionRepositoryFactory"> 
        <constructor-arg ref="flowRegistry"/> 
      </bean> 
     
      <!-- Launches new flow executions and resumes existing executions. --> 
      <bean id="flowExecutor" 
class="org.springframework.webflow.executor.FlowExecutorImpl"> 
            <constructor-arg ref="repositoryFactory"/> 
      </bean> 
 
      <bean name="phonebookFlowController" 
class="org.springframework.webflow.executor.mvc.FlowController"> 
            <property name="flowExecutor" ref="flowExecutor"/> 
            <property name="cacheSeconds" value="5"/> 
      </bean> 

此处有两个主要的 Bean:第一个 Bean,用于定义流程定义文件的位置;最后一个 Bean,用于将该 Bean 提供给 FlowController 实现。MVC FlowController 配有在执行时将此配置转换为流程的所有逻辑。

在 JSP 页面中做一些小改动

在 addEntry.jsp 页面的以下部分中最重要的部分是 _flowExecutionKey 输入参数。此键是由 SWF 生成的,并且将被作为一个隐藏参数传入参与此流程的每个页面。流程都是有状态的。结果,参与流程的视图必须将 _flowExecutionKey 参数提交回服务器以识别客户机正在参与的流程。此外,视图必须将一张可解析的 _eventId 参数表提交回服务器以通报在按下按钮时发生的事件 —— 例如,当 Submit 按钮被按下时的提交事件。

清单 9 显示了 addEntry.jsp 页面中的这些更改。

清单 9. JSP 页面中的更改

... 
<script type="text/javascript"> 
 function doSave() 
 { 
  document.myForm.pageAction.value="ADD"; 
  document.myForm._eventide.value="saveEntry"; 
  document.myForm.submit(); 
 } 
 
 function goHome() 
 { 
   document.myForm. _ eventide.value="goToHome"; 
  document.myForm.submit(); 
 } 
 
... 
... 
     
    <input type=hidden name=pageAction value=""/> 
    <input type="hidden" name="_flowExecutionKey" 
value="${flowExecutionKey}"> 
<input type="hidden" name="_eventId" value=""/> 
... 

需要对 home-dao.jsp 页面做类似更改,将使用该页面作为流程示例的主页。这十分简单。请查看 src 中的 JSP 文件。

构建并运行

就这些!已经全部完成了。运行 Ant 来构建并创建 WAR 文件。使用 Geronimo 中的 Deploy New 工具部署该文件。

需要传入流程定义名称,以便 SWF 可以执行该流程。使浏览器指向此 URL:http://localhost:8080/phonebook/phonebook.htm?_flowId=phonebookFlow。

当单击此 URL 时,SWF 将创建新流程,还将管理新流程!

当部署源文件附带的 .war 文件并指向上述 URL 时,实际上就已经查看到了本教程中讨论的所有技术的运行情况。

至此已实现了本教程设定的所有目标。

Spring 的优点

本教程中涉及的主要技术包括 Spring AOP、声明性事务管理和 SWF。每一项技术都会在 Spring 框架中添加已经备受瞩目的模块集。

下面列出了使用 Spring 声明性事务管理与使用诸如 Enterprise JavaBeans Container-Managed Transactions (EJB CMT) 这类工具相比的优势:

Spring 的事务管理不依赖于任何环境,就好像 EJB CMT 之于 Java 事务 API (JTA) 一样。它可以工作于 JDBC、Java Data Objects (JDO)、Hibernate 或其他有非常少的配置更改的事务。

Spring 的事务管理可以应用到所有传统 Jave 对象 (POJO),而不仅仅是应用到诸如 EJB 之类的特殊类。

Spring 提供了简单的声明回滚规则,这些规则在其他环境中很难实现。

可以在 Spring 中将 AOP 添加到事务顶部。

如您所见,SWF 是另一个功能强大的 Spring 模块。使用 SWF 与使用其他工具相比的优势在于:

应用程序的导航流程是在 .xml 文件中定义的,它清晰可见并且易于理解。

使用 SWF 定义的流程可在其他场景中重用。

Web Flow 的使用合同定义得非常好。它的生命周期清晰、可观察,并被自动管理。简单地说,系统将负责管理复杂性,因此它十分易于使用。

结束语

在本教程中,您了解了 Spring AOP 并尝试了 Spring AOP 所支持的声明性事务管理。AOP 很难理解和实现,但是通过方法跟踪的方面的演示,您可以看到 Spring 框架简化了使用 AOP 的过程。本教程还向您介绍了功能强大的新工具 SWF。

您看到了 Geronimo Web Console 如何使创建及管理数据库变得较为轻松,使用部署工具又如何使部署应用程序变得十分简单。在执行所有这些操作期间,您甚至一次都不需要重新启动服务器。

在第 5 部分中,将探讨最重要的 Spring 模块:Spring MVC。在先前的教程中零零碎碎地看到了此模块的运行,而在第 5 部分中您将进一步深入了解其特性和功能。

您将接触到 Spring MVC 提供的各种控制程序和处理程序。您的应用程序将被扩展以便添加更多功能,包括修改和删除电话本条目。敬请关注!

下载

描述名字大小下载方法
第 4 部分的源代码geronimo.spring4.source.zip119KBHTTP
第 4 部分的 WAR 文件geronimo.spring4.war.zip4150KBHTTP

Tags:Apache Geronimo Spring

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接