SpringAOP之在XML聲明切面

上節我們學習了,在Java類中使用注解@Aspect注解將該類聲明為一個切面,那么問題來了,如果你想聲明為切面的Java類是別人封裝好的jar包時要腫么辦,總不能該別人的源碼吧!強大的Spring會容許有這樣的Bug出現么,難道沒有解決辦法么,當然有解決方案,Spring除了可以在Java類中將其配置為切面,還可以在XML中將一個Java類配置成一個切面:

AOP元素 用途
<aop:advisor> 定義AOP通知器
<aop:after> 定義一個后置通知(不管目標方法是否執行成功)
<aop:after-returning> 定義AOP返回通知
<aop:after-throwing> 定義AOP異常通知
<aop:around> 定義環繞通知
<aop:aspect> 定義一個切面
<aop:aspectj-autoproxy> 啟動@AspectJ注解驅動的切面
<aop:before> 定義一個AOP前置通知
<aop:config> 頂層AOP配置元素。大多數的<aop:*>元素都必須包含在<aop:config>元素內
<aop:declare-parents> 以透明的方式為被通知的對象引入額外的接口
<aop:pointcut> 定義一個切點

我們之前已經看過了<aop:adpectj-autoproxy>元素,他能夠自動代理AspectJ注解的通知類。aop的其他元素可以讓我們直接在XML中配置切面,而不使用注解,下面我們定義一個不使用任何注解的Audience類:

package com.spring.aop.service.aop;

/**
 * <dl>
 * <dd>Description:觀看演出的切面</dd>
 * <dd>Company: 黑科技</dd>
 * <dd>@date:2016年9月3日 下午9:58:09</dd>
 * <dd>@author:Kong</dd>
 * </dl>
 */
@Aspect
public class Audience {

    /**
     * 目標方法執行之前調用
     */
    public void silenceCellPhone() {
        System.out.println("Silencing cell phones");
    }

    /**
     * 目標方法執行之前調用
     */
    public void takeSeats() {
        System.out.println("Taking seats");
    }

    /**
     * 目標方法執行完后調用
     */
    public void applause() {
        System.out.println("CLAP CLAP CLAP");
    }

    /**
     * 目標方法發生異常時調用
     */
    public void demandRefund() {
        System.out.println("Demanding a refund");
    }

}

現在看來Audience類和普通的Java類沒有任何的區別,但是我們只需要在Xml中稍作配置,他就可以成為一個切面。

1.聲明前置和后置通知

<aop:config>
    <aop:aspect ref="audience">
    
        <aop:before pointcut="execution(** com.spring.aop.service.Perfomance.perform(..)" 
        method="silenceCellPhone"/>
        
        <aop:before pointcut="execution(** com.spring.aop.service.Perfomance.perform(..)" 
        method="takeSeats"/>
        
        <aop:after-returning pointcut="execution(** com.spring.aop.service.Perfomance.perform(..)"
        method="applause"/>
    
        <aop:after-throwing pointcut="execution(** com.spring.aop.service.Perfomance.perform(..)"
        method="demandRefund"/>
    </aop:aspect>
</aop:config>

聰明的小伙伴一定又發現了,相同的切點我們寫了四次,這是不科學的,強大的Spring不會允許有這樣的Bug出現,你猜對了,可以使用<aop:pointcut>元素定義一個公共的切點,而且這個切點還可以定義在切面類的外邊,供其他的切面使用:

<aop:config>

    <aop:pointcut id="performance" 
        expression="execution(** com.spring.aop.service.Perfomance.perform(..)" />
        
    <aop:aspect ref="audience">
        
        <aop:before pointcut-ref="performance" method="silenceCellPhone"/>
        
        <aop:before pointcut-ref="performance" method="takeSeats"/>
        
        <aop:after-returning pointcut-ref="performance" method="applause"/>
    
        <aop:after-throwing pointcut-ref="performance"method="demandRefund"/>
    </aop:aspect>
</aop:config>

2.在Xml中配置環繞通知

上面我們介紹了在Xml中配置前置通知、后置通知等四種通知,現在我們看一下怎么在Xml中配置環繞通知,先看一下要聲明為切面的Java類中的方法:

/**
 * 環繞通知
 * @param jp 通過它調用目標方法
 */
public void watchPerformance(ProceedingJoinPoint jp) {

    try {
        System.out.println("Silencing cell phones");
        System.out.println("Taking seats");
        jp.proceed();
        System.out.println("CLAP CLAP CLAP!!!");
    } catch (Throwable e) {
        System.out.println("Demanding a refund");
    }
}

在Xml中配置環繞通知:

<aop:config>
    <aop:aspect ref="audience">
        <!-- 也可以放在<aop:aspect>外,<sop:config>內 ,可以供多個切面使用-->
    <aop:around pointcut-ref="performance" method="watchPerformance"/>
    </aop:aspect>
</aop:config>

上面我們可以看到,我們使用了<aop:around>元素聲明環繞通知。

3.為通知傳遞參數

與在使用注解配置一樣,在Xml中也可以為通知傳遞參數,先看看統計歌曲播放次數的TrackCounter類:

package com.spring.aop.service.aop;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * <dl>
 * <dd>Description:統計每首歌曲播放次數的AOP</dd>
 * <dd>Company: 黑科技</dd>
 * <dd>@date:2016年9月4日 上午8:19:24</dd>
 * <dd>@author:Kong</dd>
 * </dl>
 */
public class TrackCounter {
        
    private Map<Integer,Integer> trackCounts = new HashMap<Integer,Integer>();
    
    /**
     * 將統計播放次數的方法聲明為前置通知
     * @param trackNumber 歌曲id
     */
    public void countTrack(int trackNumber){
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount+1);
    }

    public int getPlayCount(int trackNumber) {
        return trackCounts.containsKey(trackNumber)?trackCounts.get(trackNumber):0;
    }
    
    
}

在Xml中我們可以使用aop元素,將TrackCounter配置成一個切面:

    <bean id="trackCounter" class="com.spring.aop.service.aop.TrackCounter" />  

<bean id="cd" class="com.spring.aop.service.impl.BlankDisc">
    <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band"/>
    <property name="artist" value="The Beatles"/>
    <property name="tracks">
        <list>
            <value>Sgn. Pepper's Lonelu Hears Club Band</value>
            <value>Wiith a Litter Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
        </list>
    </property>
</bean>
<aop:config>
    
    <aop:aspect ref="trackCounter">
        <aop:pointcut id="trackPlayed" 
        expression="execution(* com.spring.aop.service.CompactDisc.playTrack(int)) and args(trackNumber)"/>
        
        <aop:after pointcut-ref="trackPlayed" method="countTrack"/>
    </aop:aspect>
</aop:config>

我們注意到在定義切點的時候,我們使用的是 and 進行連接限定符,而不是&&,是因為&在Xml中要特殊的含義。

4.通過切面引入新功能

在Xml中我們可以使用<aop:declare-parents>元素為被通知的類引入新方,在Xml中的配置是這樣的:

<aop:config>
    <!--default-impl="com.spring.aop.service.impl.DefaultEncoreable":通過類名指定添加功能的實現  -->
    <!-- delegate-ref="encoreableDelegate":通過bean ID指定添加功能的實現 -->
    <aop:aspect>
        <aop:declare-parents 
        types-matching="com.spring.aop.service.Perforance+" 
        implement-interface="com.spring.aop.service.Encoreable"
        delegate-ref="encoreableDelegate"
        />
    </aop:aspect>
</aop:config>

<bean id="encoreableDelegate" class="com.spring.aop.service.impl.DefauleEncoreable"/>

可以看出<aop:declare-parents>元素包含三個屬性:

  • types-matching:指定添加方法的接口
  • mplement-interface:指定添加的新功能的接口
  • default-impl/delegate-ref:指定添加新功能接口的實現類,其中default-impl直接表示委托,delegate-ref指定一個bean,個人覺得后者較靈活。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本章內容: 面向切面編程的基本原理 通過POJO創建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,193評論 0 9
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • 陰沉慵懶的下午,老人枯瘦的身軀擱躺在一張黑色按摩椅上,窗前立著昏睡的鸚鵡。 良久,老人伸出青筋凸起的手,拿起老伴的...
    怪奇驚選集閱讀 751評論 7 12
  • “嗨,美女,XX路怎么走?” “啊?我不知道啊” “你也不是這個地方的人嗎?” “嗯嗯,是的,我不是這個地方的人”...
    販賣筆芯閱讀 359評論 0 0
  • 小時候因爸媽工作忙,無瑕照顧我們三姐妹,很小我媽就把我們送到我大舅媽那,請她來照顧我們,大舅媽家門前有囗大池塘,四...
    梅勝雪閱讀 358評論 14 7