1. 程式人生 > >Spring中AOP

Spring中AOP

動態代理

特點: 
    位元組碼隨用隨建立,隨用隨載入
作用:
    不修改原始碼的基礎上對方法增強
分類:
    1. 基於介面的動態代理
        涉及的類:
            Proxy
        提供者:
            jdk官方
        如何建立代理物件:
            使用Proxy類中的newProxyInstance方法
        建立代理物件的要求:
            被代理的類至少實現一個介面,如果沒有則不能使用
        newProxyInstance方法的引數:
            ClassLoader:類載入器
                它是用於載入代理物件位元組碼的,和被代理物件使用相同的類載入器,固定寫法
            Class[]:位元組碼陣列
                它是用於讓代理物件和被代理物件有相同的方法,固定寫法
            InvocationHandler:用於提供增強的程式碼
                它是用於讓我們寫如何代理,我們一般都是寫一個該介面的實現類,通常是一個匿名內部類(不是必須的)
                此介面的實現類都是誰用誰寫

        *存在的問題:當我們的類沒有實現任何的介面的時候是不可以使用的

        代理物件執行方法,其實就是代替被代理物件執行方法

        被代理物件實現介面
        生成代理物件
        代理物件.被代理物件的方法

    2. 基於子類的動態代理(cglib)

        涉及的類:
            Enhancer
        提供者:
            第三方cglib庫
        如何建立代理物件:
            使用Enhancer類中的create方法
        建立代理物件的要求:
            被代理的類不是最終類,不能被final修飾
        create方法的引數:
            Class:位元組碼
                它是用於指定被代理物件的位元組碼
            Callback:用於提供增強的程式碼
                它是用於讓我們寫如何代理,我們一般都是寫一個該介面的實現類,通常是一個匿名內部類(不是必須的)
                此介面的實現類都是誰用誰寫
                我們一般寫的都是該介面的子介面實現類:MethodIntercepor

Spring中的AOP

概述:
    AOP:全稱是 Aspect Oriented Programming 即:面向切面程式設計。    
    簡單的說它就是把我們程式重複的程式碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改原始碼的
    基礎上,對我們的已有方法進行增強
作用以及優勢:
    作用:
        在程式執行期間,不修改原始碼對已有方法進行增強。
    優勢:
        減少重複程式碼
        提高開發效率
        維護方便
實現方式:
    使用動態代理技術
AOP相關術語:

    連線點:
        目標物件的所有方法(所有的切入點都是連線點)
    切入點:
        被增強的都是切入點
    增強/通知:
        關於增強的程式碼塊(可以是抽取出來的類)
    引介:
        是一種特殊的通知在不修改類程式碼的前提下, Introduction 可以在執行期為類動態地新增一些方法或 Field。
    Target:
        被代理的物件
    織入:
        是指把增強應用到目標物件來建立新的代理物件的過程。
        spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝載期織入。
    代理(Proxy):
        一個類被AOP織入增強後,產生的結果代理類

    切面:
        切入點與通知的結合

Spring在AOP開發中代理方式的選擇

proxy-target-class
    預設false: 自動選擇  
    true: 使用cglib代理

1. 基於XML的
    <aop:config proxy-target-class="true">
    .....
    </aop:config>

2. 基於註解
     <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

Spring中基於XML的AOP的配置

1. 把通知bean交給Spring來管理
2. 使用aop:config標籤表明開始AOP的配置
3. 使用aop:aspect標籤表明開始配置切面
    id屬性:給切面提供一個唯一標識
    ref屬性:是指定通知類bean的id
4. 在aop:aspect標籤的內部使用對用的標籤來配置通知的型別
    前置通知:    aop:before(表示配置前置通知)
            method屬性:用於指定類中哪個方法是前置通知
            pointcut屬性:用於指定切入點表示式,該表示式的含義指的是對業務層的哪些方法增強
    切入點表示式的寫法:
        關鍵字: execution(表示式)
        表示式:
            訪問修飾符  返回值  包名.包名...類名.方法名(引數列表)
        標準表示式的寫法:
            execution(public void com.qin.service.impl.AccountServiceImpl.xxx(..))
        訪問修飾符可以省略
            void com.itheima.service.impl.AccountServiceImpl.saveAccount()
        返回值可以使用萬用字元,表示任意返回值
            * com.itheima.service.impl.AccountServiceImpl.saveAccount()
        包名可以使用萬用字元,表示任意包。但是有幾級包,就需要寫幾個*.
            * *.*.*.*.AccountServiceImpl.saveAccount())
        包名可以使用..表示當前包及其子包
            * *..AccountServiceImpl.saveAccount()
        類名和方法名都可以使用*來實現通配
            * *..*.*()
        引數列表:
            可以直接寫資料型別:
            基本型別直接寫名稱           int
            引用型別寫包名.類名的方式   java.lang.String
            可以使用萬用字元表示任意型別,但是必須有引數
            可以使用..表示有無引數均可,有引數可以是任意型別
        全通配寫法:
            * *..*.*(..)

        實際開發中切入點表示式的通常寫法:
            切到業務層實現類下的所有方法
            * com.itheima.service.impl.*.*(..)


5. 例
    <!-- 配置目標類-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>


    <!-- 配置通知類 -->
    <bean id="logger" class="com.itheima.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
    <!-- 配置切入點表示式 id屬性用於指定表示式的唯一標識。expression屬性用於指定表示式內容
          此標籤寫在aop:aspect標籤內部只能當前切面使用。
          它還可以寫在aop:aspect外面,此時就變成了所有切面可用
      -->
    <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>



    <!--配置切面 -->
    <aop:aspect id="logAdvice" ref="logger">
        <!-- 配置前置通知:在切入點方法執行之前執行-->
        <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>

        <!-- 配置後置通知:在切入點方法正常執行之後值。它和異常通知永遠只能執行一個-->
        <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"
                 returning="val"></aop:after-returning>

        <!-- 配置異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個-->
        <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1" throwing="ex"></aop:after-throwing>

        <!-- 配置最終通知:無論切入點方法是否正常執行它都會在其後面執行-->
        <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>

        <!-- 配置環繞通知 -->
        <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
    </aop:aspect>
    </aop:config>

Spring中基於註解的AOP

1. 目標物件
    即被代理的物件
        如:業務層的AccountServiceImpl
2. 通知物件
    即散落在其他類中可以抽取成共性的程式碼塊,可以提取成為一個類(比如日誌記錄,程式碼效能監控)
3. 在核心配置中開啟對AOP的支援
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4. 使用註解在通知上配置切面
    4.0 表示當前類是一個切面類
        @Aspect
    4.1 配置切點
        @Pointcut("execution(* com.itheima.service.impl.*.update(..))")
            private void pt1(){}
        @Pointcut("execution(* com.itheima.service.impl.*.save(..))")
            private void pt2(){}
    4.3 在方法上配置註解
        @Before        前置通知
        @After            最終通知
        @AfterReturning    後置通知
        @AfterThrowing    異常通知
        @Around        環繞通知