權(quán)限、事務(wù)、日志處理如果混雜在業(yè)務(wù)代碼之中會(huì)使整個(gè)代碼變得非常復(fù)雜而且可讀性很差,難以修改重構(gòu)。
面向?qū)ο蟮姆治龊驮O(shè)計(jì)引入了繼承、抽象和多態(tài)等概念,由此為我們提供了降低軟件復(fù)雜性的工具。但是,開(kāi)發(fā)人員在軟件設(shè)計(jì)過(guò)程中仍然經(jīng)常會(huì)面對(duì)無(wú)法用面向?qū)ο筌浖_(kāi)發(fā)技術(shù)輕易解決的問(wèn)題。這些問(wèn)題之一就是如何處理應(yīng)用程序中的橫切關(guān)注點(diǎn)(Cross-cutting concerns)。
1、橫切關(guān)注點(diǎn)
關(guān)注點(diǎn)就是設(shè)計(jì)人員感興趣的某一概念或區(qū)域。例如,在一個(gè)訂貨系統(tǒng)中,核心關(guān)注點(diǎn)可能是訂單處理和生產(chǎn),而系統(tǒng)關(guān)注點(diǎn)可能是事務(wù)處理和安全管理。
橫切關(guān)注點(diǎn)是影響多個(gè)類(lèi)或模塊的關(guān)注點(diǎn),即未能很好地局部化和模塊化的關(guān)注點(diǎn)。
橫切關(guān)注點(diǎn)的表現(xiàn)有:
·代碼糾結(jié)—當(dāng)一個(gè)模塊或代碼段同時(shí)管理多個(gè)關(guān)注點(diǎn)時(shí)發(fā)生這種情況。
·代碼分散—當(dāng)一個(gè)關(guān)注點(diǎn)分布在許多模塊中并且未能很好地局部化和模塊化時(shí)發(fā)生這種情況。
這些現(xiàn)象會(huì)從幾個(gè)方面影響軟件;例如,它們會(huì)導(dǎo)致軟件難以維護(hù)和重用,并且難以編寫(xiě)和理解。
2、關(guān)注點(diǎn)的隔離
面向方面編程試圖通過(guò)引入“關(guān)注點(diǎn)的隔離”這一概念來(lái)解決這些問(wèn)題。采用這一概念,可以以一種模塊化而且適當(dāng)局部化的方式實(shí)現(xiàn)關(guān)注點(diǎn)。AOP解決這個(gè)問(wèn)題的辦法是在設(shè)計(jì)空間中增加額外一維,并且引入了一些構(gòu)造,這些構(gòu)造使我們能夠定義橫切關(guān)注點(diǎn),將它們轉(zhuǎn)移進(jìn)新的維,并且以模塊化方式將它們打 包。
聲明式的AOP事務(wù)配置如下:
<bean id="nodeTarget"
class="com.daosheng.service.impl.NodeManagerImpl"
singleton="true"
lazy-init="default"
autowire="default" dependency-check="default">
<property
name="dao">
<ref
local="nodeDAO" />
</property>
</bean>
<bean
id="nodeManager"
class="com.daosheng.cms.right.RightTransactionProxyFactoryBean"
singleton="true"
lazy-init="default" autowire="default"
dependency-check="default">
<property
name="transactionManager">
<ref
local="transactionManager" />
</property>
<property
name="target">
<ref
local="nodeTarget" />
</property>
<property
name="transactionAttributes">
<props>
<prop
key="insert*">
PROPAGATION_REQUIRED,-ApplicationException
</prop>
<prop
key="update*">
PROPAGATION_REQUIRED,-ApplicationException
</prop>
</props>
</property>
</bean>
|
其實(shí)在注冊(cè)nodeManager時(shí)注冊(cè)的實(shí)際類(lèi)不是自己開(kāi)發(fā)的NodeManagerImpl類(lèi)而是注冊(cè)的RightTransactionProxyFactoryBean代理工廠(chǎng)類(lèi)。在其它類(lèi)里調(diào)用的nodeManager方法實(shí)際上是先調(diào)用的此代理工廠(chǎng)類(lèi)的加入橫切點(diǎn)代碼(事務(wù)處理代碼)然后在調(diào)用的NodeManagerImpl的相應(yīng)方法,這樣就實(shí)現(xiàn)了NodeManagerImpl類(lèi)里不用操心事務(wù)問(wèn)題,而實(shí)際的事務(wù)由加入在切點(diǎn)的Advice來(lái)完成。其在真正方法執(zhí)行前啟動(dòng)事務(wù),執(zhí)行后提交事務(wù)(commit),如果出現(xiàn)異常則回滾(rollback)。
代理工廠(chǎng)類(lèi)是采用jdk4以上自帶的動(dòng)態(tài)代理機(jī)制實(shí)現(xiàn)的。
框架的AOP中提供了4種處理切入類(lèi)型:around,before,after,introduction.顧名思義,
1) around是針對(duì)具體的某個(gè)切入點(diǎn)的方法(比如,現(xiàn)在有個(gè)OrderBook方法,around的切入類(lèi)型是就這個(gè)方法的內(nèi)部調(diào)用,是通過(guò)java的元
數(shù)據(jù),在運(yùn)行時(shí)通過(guò)Method.invoke來(lái)調(diào)用,具有返回值,當(dāng)發(fā)生意外的時(shí)候會(huì)終止.記住的一點(diǎn)是,返回值.);
2)before是在方法調(diào)用前調(diào)用(在OrderBook方法前調(diào)用,但是沒(méi)有返回值,同時(shí)在通常意外情況下,會(huì)繼續(xù)運(yùn)行下一步方法.記住的一點(diǎn)是沒(méi)有返回值);
3)after和before剛好相反,沒(méi)有什么特別的地方.
4)introduction是一個(gè)更加特殊的,但功能更加強(qiáng)大的切入類(lèi)型.比如(你現(xiàn)在有Book對(duì)象,Computer對(duì)象,還有幾十個(gè)這種業(yè)務(wù)對(duì)象,現(xiàn)在你希望在每個(gè)這樣的對(duì)象中都加入一個(gè)記錄最后修改的時(shí)間.但是你又不希望對(duì)每個(gè)類(lèi)都進(jìn)行修改,因?yàn)樘闊┝?/span>,同時(shí)更重要的一點(diǎn),破壞了對(duì)象的完整性,說(shuō)不定你以后又不需要這個(gè)時(shí)間數(shù)據(jù)了呢.框架AOP為了專(zhuān)門(mén)實(shí)現(xiàn)這種思想提供了一個(gè)切入處理,那就是introduction.introduction可以為動(dòng)態(tài)加入某些方法,這樣可以在運(yùn)行時(shí),強(qiáng)制轉(zhuǎn)換這些對(duì)象,進(jìn)行插入時(shí)間數(shù)據(jù)的動(dòng)作,更深的內(nèi)幕就是C++虛函數(shù)中的vtable思想).
下面時(shí)序圖為事務(wù)在整個(gè)各層代碼協(xié)作過(guò)程中啟動(dòng)和提交的過(guò)程。每個(gè)Manager或者DAO實(shí)體可能是開(kāi)發(fā)的實(shí)際類(lèi)也可以是代理類(lèi)。