spring知識六------AOP五大通知

要在 Spring 中聲明 AspectJ 切面, 只需要在 IOC 容器中將切面聲明爲 Bean 實例. 當在 Spring IOC 容器中初始化 AspectJ 切面之後, Spring IOC 容器就會爲那些與 AspectJ 切面相匹配的 Bean 創建代理,AOP的原理其實就是利用了動態代理,將動態代理進行了封裝。

在 AspectJ 註解中, 切面只是一個帶有 @Aspect 註解的 Java 類,通知則是標註有某種註解的簡單的 Java 方法。

AspectJ 支持 5 種類型的通知註解:
@Before: 前置通知, 在方法執行之前執行
@After: 後置通知, 在方法執行之後執行
@AfterRunning: 返回通知, 在方法返回結果之後執行
@AfterThrowing: 異常通知, 在方法拋出異常之後
@Around: 環繞通知, 圍繞着方法執行

前置通知

前置通知:在目標 方法開始之前進行執行的通知。
前置通知使用 @Before 註解, 並將切入點表達式的值作爲註解值
value屬性值:切入點表達式,匹配與之對應的目標。利用【*】可以進行那匹配不同的目標,參數只需要傳入類型即可。
方法體JoinPoint 參數:用來連接當前連接點的連接細節,一般包括方法名和參數值。【org.aspectj.lang.JoinPoint】包

@Before( value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
    public void brforelogging(JoinPoint joinPoint){ //注意 JoinPoint 的包       
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("The brforelogging methor"+ methodName+" begin with"+args);
    }

後置通知

後置通知: 在目標方法執行之後,無論是否發生異常,都進行執行的通知。
後置通知使用@After註解, 並將切入點表達式的值作爲註解值
在後置通知中,不能訪問目標方法的執行結果。原因可能在執行過程中發生異常而無法得到結果。


//  @After("execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
    public void afterlogging(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterlogging method "+methodName+" end");
    }

返回通知

返回通知: 在目標方法正常結束時,才執行的通知
返回通知使用@AfterReturning註解,並將切入點表達式的值作爲註解值
返回通知可以訪問到方法的返回值
returning屬性值:聲明該方法可以存在返回值,該屬性的值即爲用來傳入返回值的參數名稱。
方法體Object參數 :需要使用與returning同名參數名稱,用來接收方法的返回值。

@AfterReturning(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",returning="result")
    public void afterreturninglogging(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterreturninglogging method "+methodName+" end with "+result);

    }

異常通知

異常通知 :在目標方法出現異常時纔會進行執行的代碼。
異常通知使用@AfterThrowing註解,並將切入點表達式的值作爲註解值
throwing屬性:訪問連接點拋出的異常。
方法體Exception參數:用來接收連接點拋出的異常。Exception類匹配所有的異常,可以指定爲特定的異常 例如NullPointerException類等

@AfterThrowing(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",throwing="ex")
    public void afterthrowinglogging(JoinPoint joinPoint, Exception ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterthrowinglogging method "+methodName+" occurs excetion: "+ex);
    }

環繞通知

環繞通知是所有通知類型中功能最爲強大的, 能夠全面地控制連接點。甚至可以控制是否執行連接點。類似於動態代理。
環繞通知 連接點的參數類型必須是 ProceedingJoinPoint ,它是 JoinPoint 的子接口,,允許控制何時執行, 是否執行連接點
環繞通知中需要明確調用 ProceedingJoinPoint 的 proceed() 方法來執行被代理的方法,如果忘記這樣做就會導致通知被執行了,但目標方法沒有被執行
環繞通知的方法需要有返回值,返回目標方法執行之後的結果, 即調用 joinPoint.proceed() 的返回值, 否則會出現空指針異常。
環繞通知雖然功能最爲強大的,但是一般我們都不使用這個。

@Around(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
    public Object aroundlogging(ProceedingJoinPoint pjd){
        Object result =null;
        String methodName = pjd.getSignature().getName();
          List<Object> args = Arrays.asList(pjd.getArgs()); 

        try {
            // 前置通知

            System.out.println("The aroundlogging methor"+ methodName+" begin with"+args);
            result = pjd.proceed();
            // 返回通知
            System.out.println("The aroundlogging method "+methodName+" end with "+result);

        } catch (Throwable e) {
            System.out.println("The aroundlogging method "+methodName+" occurs excetion: "+e);
            // 異常通知
            e.printStackTrace();
        }
        //後置通知
        System.out.println("The aroundlogging method "+methodName+" end");
        return result;
    }

切入點表達式

在切入點表達式中我們可以利用【*】去進行模糊匹配。
最典型的就是下面的這個,我們可以根據這個進行一系列的變形。

execution(* com.wf.springaopImpl.ArithmeticCalculatorImpl.*(..))
//第一個 * 代表任意修飾符及任意返回值. 第二個 * 代表任意方法. .. 匹配任意數量的參數. 

//若目標類與接口與該切面在同一個包中, 可以省略包名.
execution(public * ArithmeticCalculator.*(..))
// 匹配 ArithmeticCalculator 接口的所有公有方法.

execution (public double ArithmeticCalculator.*(..))
//匹配 ArithmeticCalculator 中返回 double 類型數值的方法

execution (public double ArithmeticCalculator.*(double, ..))
// 匹配第一個參數爲 double 類型的方法, .. 匹配任意數量任意類型的參數

切面優先級

當在同一個方法中存在多個切面時,我們可以在註解切面的類中添加新的註解@Order,可以給我們的切面進行優先級排序,@Order(index) 其中index的值越小,所對應的優先級就越高
這裏寫圖片描述

重用切點表達式

有時候一個切點可以有多個通知,就像上面,如果我們將切點表達式寫在每一個通知中,會有大量的重複代碼。因此我們可以重用切點表達式。

// 定義一個方法,利用註解@Pointcut用於聲明切入點達式,然後在其中引入方法名即可。
// 我們可以利用ValidateAspect.declareJoinPointExpression()進行跨類聲明【類名.方法名】。
// 甚至我們可以利用【類的全路徑.方法名】使切點表達式作用於不同的包中。

public class ValidateAspect {


    @Pointcut(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
    public void declareJoinPointExpression(){
    }   

    @Before("declareJoinPointExpression()")
    public void brforeValidate(JoinPoint joinPoint){ //注意 JoinPoint 的包      
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs()); 
        System.out.println("----------The Validate methor"+ methodName+" begin with"+args);
    }

    @After("declareJoinPointExpression()")
    public void afterValidate(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterValidate method "+methodName+" end");
    }

}

基於xml的配置通知

對於沒有註解的,我們可以將對於切面的相關配置寫在我們的配置文件中。具體如下:
1、配置基本bean
2、配置切面bean
3、配置AOP
在配置AOP時,我們進行屬性配置
A、配置切面表達式
B、配置切面以及通知
注意:記得要引入aop命名空間。


<!-- 配置基本bean -->
<bean id="arithmeticCalculator" class="com.wf.springaopImplxml.ArithmeticCalculatorImpl"></bean>

<!-- 配置切面bean -->
<bean id="loggingAspect" class="com.wf.springaopImplxml.LoggingAspect"></bean>
<bean id="validateAspect" class="com.wf.springaopImplxml.ValidateAspect"></bean>
<!-- 配置AOP -->

<aop:config>
        <!-- 配置切點表達式  -->
    <aop:pointcut expression="execution(public int com.wf.springaopImplxml.ArithmeticCalculatorImpl.*(int , int))" 
    id="pointcut"/>
    <!-- 配置切面及通知 -->
    <aop:aspect ref="loggingAspect" order="2">
        <aop:before method="brforelogging" pointcut-ref="pointcut"/>
        <aop:after method="afterlogging" pointcut-ref="pointcut"/>
        <aop:after-returning method="afterreturninglogging" pointcut-ref="pointcut" returning="result"/>
        <aop:after-throwing method="afterthrowinglogging" pointcut-ref="pointcut" throwing="ex"/>
        <!-- <aop:around method="aroundlogging" pointcut-ref="pointcut"/> -->
    </aop:aspect>


    <!-- 配置切面及通知 -->
    <aop:aspect ref="validateAspect" order="0">
        <aop:before method="brforeValidate" pointcut-ref="pointcut"/>
    </aop:aspect>

</aop:config>

不再單獨上傳到博客資源,不容易通過,後期會發出百度雲鏈接或者源碼Github地址:
https://github.com/wangfa1994/SpringLearning/tree/spring005

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