1. 程式人生 > >Spring中的面向切面程式設計

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"…> ,在方法中也可以直接接收到異常物件。