Spring中的面向切面程式設計
一. 為什麼要面向切面程式設計( Aspect Oriented Programming ),或者說為什麼要用切面?
想象這樣一個場景:一個專案起初開發的時候沒有考慮日誌功能,而是在最後想為每個業務方法加上記錄日誌的功能。
如果遇到這樣的情況,是不是真的要重新編寫每一個業務方法,給它們加上日誌功能呢?
如果這樣還不能說明面向切面程式設計的必要性,那麼在考慮一個場景:一個專案由兩個專案組完成,A 組負責的是業務方法,B 組負責的是加一些日誌、安全、事務、快取等額外功能,B 組拿到的是A 組已經編譯好了的類檔案,這時再想修改原始碼很顯然是不顯示的了。而面向切面程式設計就可以解決這樣的問題。
面向切面程式設計(簡稱AOP )的目標:
1. 把橫切關注點從業務邏輯中分離,獨立模組化
2. 在不改變現有程式碼的前提下,動態的新增功能
二.Spring 框架中如何使用AOP ?
方法一:Spring 經典的AOP 實現
1. 實現MethodInceptor 介面,在其invoke() 方法中定義行為(Advice )
2. 使用ProxyFactoryBean 生成代理
<bean id="factoryBean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref=” 目標物件”></property>
<property name="interceptorNames">
<list> 切面列表</list>
</property>
</bean>
方法二:利用Spring 提供的自動代理生成器
自動代理生成器其實是在方法一的基礎上再次封裝,為我們提供了更強大的功能,同時操作也更方便。
特點:完全基於XML 配置;能夠使用AspectJ 的切點表示式。
示例:
1. 配置檔案中新增aop 名稱空間,完整的根節點定義如下:
<beans
</beans>
2. 新增如下配置:
<aop:config>
<aop:pointcut id="pc"
expression="within( 選擇要切的類)"/>
<aop:aspect ref=" 切面">
<aop:before pointcut-ref="pc" method=" 切面中的方法"/>
</aop:aspect>
</aop:config>
除了aop:before 外,還有多種Advice
before 表示 切面在切點之前;
after-returning 表示 切點方法執行完畢成功返回後執行切面方法;
after-throwing 表示 切點方法丟擲異常時執行切面方法;
after 表示 無論切點方法是否成功返回,都執行切面方法;
around 表示 切點方法執行期間執行切面方法,也就是自定義執行順
序。
around 對方法有如下要求:
1, 返回型別Object
2, 引數ProceedingJoinPoint
3,throws Throwable
AspectJ 切點表示式:
within: 匹配類內的所有方法(必須是實現類,不能是介面)
如 :within(first.Singer)
execution: 匹配指定的方法
execution(void perform()) 匹配專案下所有該方法
execution(void first.Singer.perform()) 匹配具體到某個類的該方法
execution(* first.Artist.perform()) 不考慮返回值型別
execution(* first.Artist.perform(..)) 不考慮返回值型別和引數列表
execution(* first.Aritst.perform(*,*)) 引數必須是兩個
execution(* first.Artist.perform(..,java.lang.String))
execution(* find*(..)) 所有方法名符合findXxx 的方法
execution(* com.tarena.service.StoreService.*(..)) 該類中所有方法
execution(* com.tarena.service.*.*(..)) 該包中所有類的所有方法
execution(* com.tarena..*.*(..)) 該包及其子包中所有類的所有方法
條件運算子:not and or
within(first.service.StoreService) or execution(* first.dao.*.*(..))
三.切面方法中如何獲得切點資訊?
around 對方法要求有引數ProceedingJoinPoint ,所以可以很容易的獲得切點物件的相關資訊。那麼after, before 等其他的切面型別對方法沒有這樣的要求,該怎麼獲得切點資訊呢?
對於這些方法,可以直接為其新增JoinPoint 引數,這樣就可以獲得
方法物件,引數列表,目標物件,代理物件 的資訊。
返回值和異常則可以直接接受,然後在配置中新增屬性即可。例如:
方法簽名為:public void after(JoinPoint jp, Object res, Exception e)
配置資訊為:<aop:after-returning returning="res" …> 則在方法中可以直接使用切點方法返回的物件res ;同樣 < aop:after-throwing throwing="e"…> ,在方法中也可以直接接收到異常物件。