官术网_书友最值得收藏!

2.3 面向切面編程

本節主要認識橫切、縱切,理解什么是AOP(Aspect-Oriented Programming,面向切面編程)以及AOP的實現原理。在此基礎上通過示例動手操作加深理解。

2.3.1 認識橫切和縱切

首先認識一下什么是橫切、縱切,這就要利用生物方面的知識了。切面的方向是這樣規定的:拿植物的莖舉例,縱切就是沿長軸來切,橫切即是垂直與縱切的切法。編程相對來說是比較抽象的,有時候我們通過身邊的事物來將抽象的具體化,這樣也能更容易理解。

利用百度搜索“橫切”“縱切”時,首先搜出來的結果是剖宮產的結果,內容是這樣描述的:“由于人體和血管、神經系統等都是縱向的走向,所以縱切更有利于皮膚和傷口的愈合,通常在1年到2年就可以徹底恢復。但縱切的缺點是傷口較大,會留下明顯的疤痕,會影響美觀。橫切的方法出血較少,并且出現傷口感染的機率要低于縱切,所以安全性比縱切高一些,只是不利再次進行剖宮產手術。”這里的橫切、縱切與編程中的還是挺相似的。

2.3.2 什么是AOP

AOP,可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行為的一個集合。當我們需要為分散的對象引入公共行為(日志、安全、事務)的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關系,但并不適合定義從左到右的關系。例如,日志功能。日志代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關系。對于其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散布在各處的無關的代碼被稱為橫切(cross-cutting)代碼。在OOP設計中,它導致了大量代碼重復、模塊間的藕合度高,不利于各個模塊的重用。

AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的對象內部,并將那些影響了多個類的公共行為封裝到一個可重用模塊,并將其命名為“Aspect”,即切面。所謂“切面”,簡單地說,就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便于減少系統的重復代碼,降低模塊間的耦合度,并有利于未來的可操作性和可維護性。

2.3.3 AOP原理

AOP實際上是由目標類的代理類實現的。AOP代理其實是由AOP框架動態生成的一個對象,該對象可作為目標對象使用。AOP代理包含了目標對象的全部方法(見圖2-3),但AOP代理中的方法與目標對象的方法存在差異,AOP方法在特定切入點添加了增強處理,并回調了目標對象的方法。

圖2-3

由于是代理實現AOP,因此有必要學習一下代理。下面通過實例一步一步地了解靜態代理和動態代理。新建一個ServiceImplA類,實現IService接口,想在調用service方法前后增加日志打印或為service方法增加try catch,那么該怎么做呢?

1.在每處調用的地方增加日志和try catch

這也是一種方法,但缺點是很明顯的,就是每處都要更改,量也會很大,顯然不可取。這里是每個點都要加,一個方法可能被調用多處,就要寫多次。而且以后再進行修改時也不方便,每個地方都要修改。

2.代理模式

代理模式又分為動態代理模式和靜態代理模式。

(1)靜態代理

靜態代理關鍵是在代理對象和目標對象實現共同的接口,并且代理對象持有目標對象的引用。這里用類ProxyServiceA來實現IService接口,同時將實現IService接口的對象作為一個屬性。

輸出結果:

     log start
     ServiceImplA service:CYW
     log end

有了ProxyServiceA之后,打印日志和增加try-catch只需放在ProxyServiceA類里面,便于后續修改,比如現在打印日志是輸出在操作臺的,哪天需要輸入到日志文件時也只需修改ProxyServiceA中的打印操作即可。但問題來了:項目中的接口可不止一個,可能會有很多,而且每個接口中的方法也會有好多,這樣一個一個地增加也是問題,于是有了動態代理。

(2)動態代理

在Java的動態代理機制中,有兩個重要的類或接口:一個是InvocationHandler(Interface),另一個是Proxy(Class)。這一個類和接口是實現動態代理所必須用到的。

輸出結果:

通過Proxy.newProxyInstance()生成的動態代理對象A都會與實現InvocationHandler接口的對象B關聯,動態代理對象A調用目標對象方法時都會變成調用B中的invoke方法。在invoke方法中織入增強處理,并通過反射回調目標對象方法。在本例中,通過bind()生成目標對象ServiceImplA的動態代理對象A,A關聯了實現InvocationHandler接口對象的DynaProxyServiceA,當動態代理對象A調用目標對象方法時會執行DynaProxyServiceA的invoke方法,增加try-catch、打印日志,并回調目標對象的方法。

與前面的靜態代理比較發現,動態代理不用再為每個接口手動創建代理類,其他對象只要與InvocationHandler接口對象bind,就能獲得該InvocationHandler接口對象的織入增強。

主站蜘蛛池模板: 宝兴县| 英山县| 五河县| 莱阳市| 民丰县| 崇阳县| 黄龙县| 新安县| 禄丰县| 吕梁市| 监利县| 同心县| 陵川县| 卓资县| 安图县| 中山市| 刚察县| 嘉义县| 城市| 敖汉旗| 炉霍县| 淅川县| 通河县| 库伦旗| 平武县| 彭州市| 乌鲁木齐市| 靖宇县| 永昌县| 志丹县| 镇江市| 晋中市| 江口县| 吉安市| 乐至县| 尚义县| 青龙| 资溪县| 丹阳市| 卢氏县| 荃湾区|