天天看點

我的Spring之旅——(三)AOP通知的幾種類型

為了搞明白AOP通知的類型之間有什麼差別,我也是走了一些小彎路,下面就把我遇見的坑坑窪窪扒拉出來涼一涼吧~

一、AOP的通知類型

1.前置通知(before advice):在連接配接點前面執行,對連接配接點不會造成影響(前置通知有異常的話,會對後續操作有影響)

2.正常傳回通知(after returning advice):在連接配接點正确執行之後執行,如果連接配接點抛異常,則不執行

3.異常傳回通知(after throw Advice):在連接配接點抛異常的時候執行

4.傳回通知(after):無論連接配接點是正确執行還是抛異常,都會執行

5.環繞通知(around):在連接配接點前後執行(必須在環繞通知中決定是繼續處理還是中斷執行,使用PreceedingJoinPonit下的方法決定是繼續還是中斷)

注:這裡我不得不說一下了,上面第五條中紅色标注的地方非常非常非常重要,菜鳥(哦不,可能笨鳥更适合我)就在這翻了一個很大的跟頭

我的Spring之旅——(三)AOP通知的幾種類型

二、兩種實作方式

方法一:配置檔案法

1.業務類

package lm.practice.services;

/**
 * Created by Administrator on 2017/4/13.
 */


/**
 * 業務實作類
 */

public class BankService {
    /**
     * 模拟銀行轉賬系統
     * @param form  轉出者
     * @param to  轉入者
     * @param count 金額
     * @return
     */
    public boolean transfer(String form, String to, double count) {
        if(count<100.0){
            throw new IllegalArgumentException("最低轉賬金額不得少于100");
        }
        System.out.println(form+"向"+to+"中行賬戶轉賬"+count);
        return false;
    }
}      

2.切面類

package lm.practice.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * Created by Administrator on 2017/4/13.
 */

/**
 * 切面類
 */
public class MyAdvices {

    /**
     * 前置通知
     * @param joinPoint 連接配接點
     */
    public void before(JoinPoint joinPoint){
        System.out.print("--------前置通知----------");
    }

    /**
     * 正常傳回通知
     * @return
     */
    public boolean afterReturning(){
        System.out.println("------正常傳回通知--------");
        return true;
    }

    /**
     * 異常傳回通知
     * @return
     */
    public void afterThrow(){
        System.out.print("-------異常傳回通知-------");
    }

    /**
     * 傳回通知
     * @return
     */
    public void after(){
        System.out.println("----------傳回通知--------");
    }

    /**
     * 環繞通知
     * @return
     */
    public boolean around(ProceedingJoinPoint joinPoint) throws Throwable {
       System.out.println("--------環繞通知----------");
       oinPoint.proceed();
       return true;
    }
}
      

3.配置檔案

<?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="bankService" class="lm.practice.services.BankService">
    </bean>

    <!--通知-->
    <bean id="myAdvices" class="lm.practice.aop.MyAdvices"></bean>

    <!--aop配置-->
    <aop:config proxy-target-class="true">
        <!--切面-->
        <aop:aspect ref="myAdvices">
            <!--切點-->
            <aop:pointcut id="pointcut"  expression="execution(* lm.practice.services.*.*(..))"></aop:pointcut>
            <!--連接配接通知方法與切點-->
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>
            <aop:after-throwing method="afterThrow" pointcut-ref="pointcut"></aop:after-throwing>
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
            <aop:around method="around" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>      

4.測試類

package test;

import lm.practice.services.BankService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * Created by Administrator on 2017/4/13.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-aop-bank.xml")
public class BankServiceTest {
    @Autowired
    private BankService bankService;
    @Test
    public void test(){

       bankService.transfer("商行","中行",100.00);
    }      

4.運作結果

我的Spring之旅——(三)AOP通知的幾種類型

如果轉賬金額小于100的情況下,輸出結果:

我的Spring之旅——(三)AOP通知的幾種類型

方法二、注解法

可以參照我之前寫的部落格自己試着進行

-------------------------------------------------------------------------以上是正确的代碼---------------------------------------------------------

我現在就說一下我犯的一個錯誤:

package lm.practice.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * Created by Administrator on 2017/4/13.
 */

/**
 * 切面類
 */
public class MyAdvices {

    /**
     * 前置通知
     * @param joinPoint 連接配接點
     */
    public void before(JoinPoint joinPoint){
        System.out.print("--------前置通知----------");
    }

    /**
     * 正常傳回通知
     * @return
     */
    public boolean afterReturning(){
        System.out.println("------正常傳回通知--------");
        return true;
    }

    /**
     * 異常傳回通知
     * @return
     */
    public void afterThrow(){
        System.out.print("-------異常傳回通知-------");
    }

    /**
     * 傳回通知
     * @return
     */
    public void after(){
        System.out.println("----------傳回通知--------");
    }
    
    /**
     * 環繞通知
     */
    public void around(){
        System.out.println("--------環繞通知----------");
    }}      

這是我犯錯的時候的切面類的書寫方法,報錯資訊如下:

我的Spring之旅——(三)AOP通知的幾種類型

org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean lm.practice.services.BankService.transfer(java.lang.String,java.lang.String,double)

上面的就是控制台中那段很長的代碼:通知的傳回為空,與注入方法傳回類型不比對。

-------------------------------------------------------我的漫漫求解路----------------------------------------------------------

第一次嘗試:首先我以為單純的是傳回類型的問題,就把切面類中的異常傳回通知(afterReturning)傳回值類型改成了boolen,結果不可行,依舊報錯

第二次嘗試:将不同通知類型分開寫(沒有完成,被導師否定,因為根源不在這)

第三次嘗試:将配置檔案的方式改成注解,還是不行

第四次嘗試:導師捉急啊,直接一步一步引導我,說要抛開表面看實質,他是這樣引導我的:首先問我每個通知類型的差別,然後問我around和after和before有什麼差別,然後問我知道around有什麼注意點嗎,最後讓我查一下,既然是around(環繞),那麼怎麼确定是不是會執行連接配接點呢?最後我查了一下,發現在使用around的時候必須要決定是繼續執行還是中斷執行,然後我這隻笨鳥就在around中寫了那麼一段繼續執行的代碼,然後問題解決了

總結一下就是around必須要指定是繼續執行還是中斷執行(重要!重要!!重要!!!重要!!!!!)

到此我的spring學習就告一段落了,這裡還有一個重要的知識點就是日志了,我這裡用到的是log4j,具體的一些知識點,後續會分享出來~

呼呼~~下班啦

我的Spring之旅——(三)AOP通知的幾種類型