Spring--Spring AOP 面向切面編程

目錄

一、AOP——另一種編程思想

1.1 什麼是 AOP

1.2 爲什麼需要 AOP

1.3 AOP 實現分類

二、AOP 術語

三、初步認識 Spring AOP

3.1 Spring AOP 的特點

3.2 Spring AOP 的簡單例子

四、通過註解配置 Spring AOP

4.1 通過註解聲明切點指示器

4.2 通過註解聲明 5 種通知類型

4.3 通過註解聲明切點表達式

4.4 通過註解處理通知中的參數

4.5 通過註解配置織入的方式

五、通過 XML 配置文件聲明切面

5.1 XML 配置文件中切點指示器

5.2 XML 文件配置 AOP 實例

5.3 XML 文件配置聲明切點

5.4 XML文件配置爲通知傳遞參數

5.5 Xml 文件配置織入的方式

六、總結


以下文章來源於https://www.cnblogs.com/joy99/p/10941543.html,作者SharpCJ

一、AOP——另一種編程思想

1.1 什麼是 AOP

AOP (Aspect Orient Programming),直譯過來就是 面向切面編程。AOP 是一種編程思想,是面向對象編程(OOP)的一種補充。面向對象編程將程序抽象成各個層次的對象,而面向切面編程是將程序抽象成各個切面。
從《Spring實戰(第4版)》圖書中扒了一張圖:

從該圖可以很形象地看出,所謂切面,相當於應用對象間的橫切點,我們可以將其單獨抽象爲單獨的模塊。

1.2 爲什麼需要 AOP

想象下面的場景,開發中在多個模塊間有某段重複的代碼,我們通常是怎麼處理的?顯然,沒有人會靠“複製粘貼”吧。在傳統的面向過程編程中,我們也會將這段代碼,抽象成一個方法,然後在需要的地方分別調用這個方法,這樣當這段代碼需要修改時,我們只需要改變這個方法就可以了。然而需求總是變化的,有一天,新增了一個需求,需要再多出做修改,我們需要再抽象出一個方法,然後再在需要的地方分別調用這個方法,又或者我們不需要這個方法了,我們還是得刪除掉每一處調用該方法的地方。實際上涉及到多個地方具有相同的修改的問題我們都可以通過 AOP 來解決。

1.3 AOP 實現分類

AOP 要達到的效果是,保證開發者不修改源代碼的前提下,去爲系統中的業務組件添加某種通用功能。AOP 的本質是由 AOP 框架修改業務組件的多個方法的源代碼,看到這其實應該明白了,AOP 其實就是前面一篇文章講的代理模式的典型應用。
按照 AOP 框架修改源代碼的時機,可以將其分爲兩類:

  • 靜態 AOP 實現, AOP 框架在編譯階段對程序源代碼進行修改,生成了靜態的 AOP 代理類(生成的 *.class 文件已經被改掉了,需要使用特定的編譯器),比如 AspectJ。
  • 動態 AOP 實現, AOP 框架在運行階段對動態生成代理對象(在內存中以 JDK 動態代理,或 CGlib 動態地生成 AOP 代理類),如 SpringAOP。

下面給出常用 AOP 實現比較

如不清楚動態代理的,可參考我前面的一篇文章,有講解靜態代理、JDK動態代理和 CGlib 動態代理。

靜態代理和動態代理 https://www.cnblogs.com/joy99/p/10865391.html

二、AOP 術語

AOP 領域中的特性術語:

  • 通知(Advice): AOP 框架中的增強處理。通知描述了切面何時執行以及如何執行增強處理。
  • 連接點(join point): 連接點表示應用執行過程中能夠插入切面的一個點,這個點可以是方法的調用、異常的拋出。在 Spring AOP 中,連接點總是方法的調用。
  • 切點(PointCut): 可以插入增強處理的連接點。
  • 切面(Aspect): 切面是通知和切點的結合。
  • 引入(Introduction):引入允許我們向現有的類添加新的方法或者屬性。
  • 織入(Weaving): 將增強處理添加到目標對象中,並創建一個被增強的對象,這個過程就是織入。

概念看起來總是有點懵,並且上述術語,不同的參考書籍上翻譯還不一樣,所以需要慢慢在應用中理解。

三、初步認識 Spring AOP

3.1 Spring AOP 的特點

AOP 框架有很多種,1.3節中介紹了 AOP 框架的實現方式有可能不同, Spring 中的 AOP 是通過動態代理實現的。不同的 AOP 框架支持的連接點也有所區別,例如,AspectJ 和 JBoss,除了支持方法切點,它們還支持字段和構造器的連接點。而 Spring AOP 不能攔截對對象字段的修改,也不支持構造器連接點,我們無法在 Bean 創建時應用通知。

3.2 Spring AOP 的簡單例子

下面先上代碼,對着代碼說比較好說,看下面這個例子:
這個例子是基於gradle創建的,首先 build.gradle 文件添加依賴:

dependencies {
    compile 'org.springframework:spring-context:5.0.6.RELEASE'
}

首先創建一個接口 IBuy.java

package com.sharpcj.aopdemo.test1;

public interface IBuy {
    String buy();
}

Boy 和 Gril 兩個類分別實現了這個接口:
Boy.java

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Boy implements IBuy {
    @Override
    public String buy() {
        System.out.println("男孩買了一個遊戲機");
        return "遊戲機";
    }
}

Girl.java

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Girl implements IBuy {
    @Override
    public String buy() {
        System.out.println("女孩買了一件漂亮的衣服");
        return "衣服";
    }
}

配置文件, AppConfig.java

package com.sharpcj.aopdemo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackageClasses = {com.sharpcj.aopdemo.test1.IBuy.class})
public class AppConfig {
}

測試類, AppTest.java

package com.sharpcj.aopdemo;

import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Boy boy = context.getBean("boy",Boy.class);
        Girl girl = (Girl) context.getBean("girl");
        boy.buy();
        girl.buy();
    }
}

運行結果:

這裏運用SpringIOC裏的自動部署。現在需求改變了,我們需要在男孩和女孩的 buy 方法之前,需要打印出“男孩女孩都買了自己喜歡的東西”。用 Spring AOP 來實現這個需求只需下面幾個步驟:
1、 既然用到 Spring AOP, 首先在 build.gralde 文件中引入相關依賴:

dependencies {
    compile 'org.springframework:spring-context:5.0.6.RELEASE'
    compile 'org.springframework:spring-aspects:5.0.6.RELEASE'
}

2、 定義一個切面類,BuyAspectJ.java

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {
    @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void haha(){
        System.out.println("男孩女孩都買自己喜歡的東西");
    }
}

這個類,我們使用了註解 @Component 表明它將作爲一個Spring Bean 被裝配,使用註解 @Aspect 表示它是一個切面
類中只有一個方法 haha 我們使用 @Before 這個註解,表示他將在方法執行之前執行。關於這個註解後文再作解釋。
參數("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))") 聲明瞭切點,表明在該切面的切點是com.sharpcj.aopdemo.test1.Ibuy這個接口中的buy方法。至於爲什麼這麼寫,下文再解釋。
3、 在配置文件中啓用AOP切面功能

package com.sharpcj.aopdemo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackageClasses = {com.sharpcj.aopdemo.test1.IBuy.class})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}

我們在配置文件類增加了@EnableAspectJAutoProxy註解,啓用了 AOP 功能,參數proxyTargetClass的值設爲了 true 。默認值是 false,兩者的區別下文再解釋(4.5節)。
OK,下面只需測試代碼,運行結果如下:

我們看到,結果與我們需求一致,我們並沒有修改 Boy 和 Girl 類的 Buy 方法,也沒有修改測試類的代碼,幾乎是完全無侵入式地實現了需求。這就是 AOP 的“神奇”之處

四、通過註解配置 Spring AOP

4.1 通過註解聲明切點指示器

Spring AOP 所支持的 AspectJ 切點指示器

在spring中嘗試使用AspectJ其他指示器時,將會拋出IllegalArgumentException異常。

當我們查看上面展示的這些spring支持的指示器時,注意只有execution指示器是唯一的執行匹配,而其他的指示器都是用於限制匹配的。這說明execution指示器是我們在編寫切點定義時最主要使用的指示器,在此基礎上,我們使用其他指示器來限制所匹配的切點。

下圖的切點表達式表示當Instrument的play方法執行時會觸發通知。

我們使用execution指示器選擇Instrument的play方法

  1. 方法表達式以 * 號開始,標識我們不關心方法的返回值類型
  2. 然後我們指定了全限定類名和方法名。對於方法參數列表,我們使用 .. 標識切點選擇任意的play方法,無論該方法的入參是什麼。
  3. 多個匹配之間我們可以使用鏈接符 &&||來表示 “且”、“或”、“非”的關係
  4. 但是在使用 XML 文件配置時,這些符號有特殊的含義,所以我們使用 “and”、“or”、“not”來表示。

舉例:

限定該切點僅匹配的包是 com.sharpcj.aopdemo.test1,可以使用
execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && within(com.sharpcj.aopdemo.test1.*)
在切點中選擇 bean,可以使用
execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && bean(girl)

修改 BuyAspectJ.java

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {
    @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && within(com.sharpcj.aopdemo.test1.*) && bean(girl)")
    public void hehe(){
        System.out.println("男孩女孩都買自己喜歡的東西");
    }
}

此時,切面只會對 Girl.java 這個類生效,執行結果:

細心的你,可能發現了,切面中的方法名,已經被我悄悄地從haha改成了hehe,絲毫沒有影響結果,說明方法名沒有影響。和 Spring IOC 中用 java 配置文件裝配 Bean 時,用@Bean 註解修飾的方法名一樣,沒有影響

4.2 通過註解聲明 5 種通知類型

Spring AOP 中有 5 中通知類型,分別如下:

下面修改切面類:

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {
    @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

}

爲了方便看效果,我們測試類中,只要 Boy 類:

package com.sharpcj.aopdemo;

import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Boy boy = context.getBean("boy",Boy.class);
        Girl girl = (Girl) context.getBean("girl");
        boy.buy();
        // girl.buy();
    }
}

執行結果如下:

結果顯而易見。指的注意的是 @Around 修飾的環繞通知類型,是將整個目標方法封裝起來了,在使用時,我們傳入了 ProceedingJoinPoint 類型的參數,這個對象是必須要有的,並且需要調用 ProceedingJoinPoint 的 proceed() 方法。 如果沒有調用 該方法,執行結果爲 :

Around aaa ...
Around bbb ...
After ...
AfterReturning ...

可見,如果不調用該對象的 proceed() 方法,表示原目標方法被阻塞調用,當然也有可能你的實際需求就是這樣。

4.3 通過註解聲明切點表達式

如你看到的,上面我們寫的多個通知使用了相同的切點表達式,對於像這樣頻繁出現的相同的表達式,我們可以使用 @Pointcut註解聲明切點表達式,然後使用表達式,修改代碼如下:
BuyAspectJ.java

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {

    @Pointcut("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void point(){}

    @Before("point()")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("point()")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("point()")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("point()")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

程序運行結果沒有變化。
這裏,我們使用

@Pointcut("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
public void point(){}

聲明瞭一個切點表達式,該方法 point 的內容並不重要,方法名也不重要,實際上它只是作爲一個標識,供通知使用

4.4 通過註解處理通知中的參數

上面的例子,我們要進行增強處理的目標方法沒有參數,下面我們來說說有參數的情況,並且在增強處理中使用該參數。
下面我們給接口增加一個參數,表示購買所花的金錢。通過AOP 增強處理,如果女孩買衣服超過了 68 元,就可以贈送一雙襪子。
更改代碼如下:
IBuy.java

package com.sharpcj.aopdemo.test1;

public interface IBuy {
    String buy(double price);
}

Girl.java

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Girl implements IBuy {
    @Override
    public String buy(double price) {
        System.out.println(String.format("女孩花了%s元買了一件漂亮的衣服", price));
        return "衣服";
    }
}

Boy.java

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Boy implements IBuy {
    @Override
    public String buy(double price) {
        System.out.println(String.format("男孩花了%s元買了一個遊戲機", price));
        return "遊戲機";
    }
}

再看 BuyAspectJ 類,我們將之前的通知都註釋掉。用一個環繞通知來實現這個功能:

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {

    /*
    @Pointcut("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void point(){}

    @Before("point()")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("point()")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("point()")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("point()")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    */


    @Pointcut("execution(String com.sharpcj.aopdemo.test1.IBuy.buy(double)) && args(price) && bean(girl)")
    public void gif(double price) {
    }

    @Around("gif(price)")
    public String hehe(ProceedingJoinPoint pj, double price){
        try {
            pj.proceed();
            if (price > 68) {
                System.out.println("女孩買衣服超過了68元,贈送一雙襪子");
                return "衣服和襪子";
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return "衣服";
    }
}

前文提到,當不關心方法返回值的時候,我們在編寫切點指示器的時候使用了 * , 當不關心方法參數的時候,我們使用了 ..。現在如果我們需要傳入參數,並且有返回值的時候,則需要使用對應的類型。在編寫通知的時候,我們也需要聲明對應的返回值類型和參數類型

測試類:AppTest.java

package com.sharpcj.aopdemo;

import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Boy boy = context.getBean("boy",Boy.class);
        Girl girl = (Girl) context.getBean("girl");
        String boyBought = boy.buy(35);
        String girlBought = girl.buy(99.8);

        System.out.println("男孩買到了:" + boyBought);
        System.out.println("女孩買到了:" + girlBought);
    }
}

測試結果:

可以看到,我們成功通過 AOP 實現了需求,並將結果打印了出來。

4.5 通過註解配置織入的方式

前面還有一個遺留問題,在配置文件中,我們用註解 @EnableAspectJAutoProxy() 啓用Spring AOP 的時候,我們給參數 proxyTargetClass 賦值爲 true,如果我們不寫參數,默認爲 false。這個時候運行程序,程序拋出異常

這是一個強制類型轉換異常。爲什麼會拋出這個異常呢?或許已經能夠想到,這跟Spring AOP 動態代理的機制有關,這個 proxyTargetClass 參數決定了代理的機制。

當這個參數爲 false 時,通過jdk的基於接口的方式進行織入,這時候代理生成的是一個接口對象,將這個接口對象強制轉換爲實現該接口的一個類,自然就拋出了上述類型轉換異常。
反之,proxyTargetClass 爲 true,則會使用 cglib 的動態代理方式。這種方式的缺點是拓展類的方法被final修飾時,無法進行織入。

測試一下,我們將 proxyTargetClass 參數設爲 true,同時將 Girl.java 的 Buy 方法用 final 修飾:
AppConfig.java

package com.sharpcj.aopdemo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackageClasses = {com.sharpcj.aopdemo.test1.IBuy.class})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}

Girl.java

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Girl implements IBuy {
    @Override
    public final String buy(double price) {
        System.out.println(String.format("女孩花了%s元買了一件漂亮的衣服", price));
        return "衣服";
    }
}

此時運行結果:

可以看到,我們的切面並沒有織入生效。

五、通過 XML 配置文件聲明切面

前面的示例中,我們已經展示瞭如何通過註解配置去聲明切面,下面我們看看如何在 XML 文件中聲明切面。下面先列出 XML 中聲明 AOP 的常用元素:

我們依然可以使用 <aop:aspectj-autoproxy> 元素,他能夠自動代理AspectJ註解的通知類。

5.1 XML 配置文件中切點指示器

在XML配置文件中,切點指示器表達式與通過註解配置的寫法基本一致,區別前面有提到,即XML文件中需要使用 “and”、“or”、“not”來表示 “且”、“或”、“非”的關係。

5.2 XML 文件配置 AOP 實例

下面我們不使用任何註解改造上面的例子:
BuyAspectJ.java

package com.sharpcj.aopdemo.test2;

import org.aspectj.lang.ProceedingJoinPoint;

public class BuyAspectJ {

    public void hehe() {
        System.out.println("before ...");
    }

    public void haha() {
        System.out.println("After ...");
    }

    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

在 Resource 目錄下新建一個配置文件 aopdemo.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="boy" class="com.sharpcj.aopdemo.test2.Boy"></bean>
    <bean id="girl" class="com.sharpcj.aopdemo.test2.Girl"></bean>
    <bean id="buyAspectJ" class="com.sharpcj.aopdemo.test2.BuyAspectJ"></bean>

    <aop:config proxy-target-class="true">
        <aop:aspect id="qiemian" ref="buyAspectJ">
            <aop:before pointcut="execution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))" method="hehe"/>
            <aop:after pointcut="execution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))" method="haha"/>
            <aop:after-returning pointcut="execution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))" method="xixi"/>
            <aop:around pointcut="execution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))" method="xxx"/>
        </aop:aspect>
    </aop:config>
</beans>

這裏分別定義了一個切面,裏面包含四種類型的通知。
測試文件中,使用

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aopdemo.xml");

來獲取 ApplicationContext,其它代碼不變。

5.3 XML 文件配置聲明切點

對於頻繁重複使用的切點表達式,我們也可以聲明成切點。
配置文件如下:aopdemo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="boy" class="com.sharpcj.aopdemo.test2.Boy"></bean>
    <bean id="girl" class="com.sharpcj.aopdemo.test2.Girl"></bean>
    <bean id="buyAspectJ" class="com.sharpcj.aopdemo.test2.BuyAspectJ"></bean>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="apoint" expression="execution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))"/>
        <aop:aspect id="qiemian" ref="buyAspectJ">
            <aop:before pointcut-ref="apoint" method="hehe"/>
            <aop:after pointcut-ref="apoint" method="haha"/>
            <aop:after-returning pointcut-ref="apoint" method="xixi"/>
            <aop:around pointcut-ref="apoint" method="xxx"/>
        </aop:aspect>
    </aop:config>
</beans>

5.4 XML文件配置爲通知傳遞參數

BuyAspectJ.java

package com.sharpcj.aopdemo.test2;

import org.aspectj.lang.ProceedingJoinPoint;

public class BuyAspectJ {
public String hehe(ProceedingJoinPoint pj, double price){
        try {
            pj.proceed();
            if (price > 68) {
                System.out.println("女孩買衣服超過了68元,贈送一雙襪子");
                return "衣服和襪子";
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return "衣服";
    }
}

aopdemo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="boy" class="com.sharpcj.aopdemo.test2.Boy"></bean>
    <bean id="girl" class="com.sharpcj.aopdemo.test2.Girl"></bean>
    <bean id="buyAspectJ" class="com.sharpcj.aopdemo.test2.BuyAspectJ"></bean>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="apoint" expression="execution(String com.sharpcj.aopdemo.test2.IBuy.buy(double)) and args(price) and bean(girl)"/>
        <aop:aspect id="qiemian" ref="buyAspectJ">
            <aop:around pointcut-ref="apoint" method="hehe"/>
        </aop:aspect>
    </aop:config>
</beans>

5.5 Xml 文件配置織入的方式

同註解配置類似,
CGlib 代理方式:

<aop:config proxy-target-class="true"> </aop:config>

JDK 代理方式:

<aop:config proxy-target-class="false"> </aop:config>

六、總結

本文簡單記錄了 AOP 的編程思想,然後介紹了 Spring 中 AOP 的相關概念,以及通過註解方式和XML配置文件兩種方式使用 Spring AOP進行編程。 相比於 AspectJ 的面向切面編程,Spring AOP 也有一些侷限性,但是已經可以解決開發中的絕大多數問題了,如果確實遇到了 Spring AOP 解決不了的場景,我們依然可以在 Spring 中使用 AspectJ 來解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章