怎麼才能讓Spring AOP有最大的做用--樂字節java

Spring AOP

日誌處理帶來的問題

​ 咱們有一個Pay(接口) 而後兩個實現類DollarPay和RmbPay,都須要重寫pay()方法, 這時咱們須要對pay方法進行性能監控,日誌的添加等等怎麼作?java

SpringAOP-02

最容易想到的方法

​ 對每一個字符方法均作日誌代碼的編寫處理,以下面方式spring

SpringAOP-03

​ 缺點: 代碼重複太多, 添加的日誌代碼耦合度過高(若是須要更改日誌記錄代碼功能需求,類中方法須要所有改動,工程量浩大)express

使用裝飾器模式 /代理模式改進解決方案

裝飾器模式:動態地給一個對象添加一些額外的職責。編程

代理模式:以上剛講過。因而得出如下結構:安全

SpringAOP-04

​ 仔細考慮事後發現雖然對原有內部代碼沒有進行改動,對於每一個類作日誌處理,並引用目標類,可是若是待添加日誌的業務類的數量不少,此時手動爲每一個業務類實現一個裝飾器或建立對應的代理類,同時代碼的耦合度也加大,需求一旦改變,改動的工程量也是可想而知的。ide

有沒有更好的解決方案,只要寫一次代碼,對想要添加日誌記錄的地方可以實現代碼的複用,達到鬆耦合的同時,又可以完美完成功能?oop

​ 答案是確定的,存在這樣的技術,aop已經對其提供了完美的實現!性能

若有疑問,可加入羣:10803-55292,輸入暗號13,便可有大佬幫助代理

什麼是AOP?

​ Aspect Oriented Programing 面向切面編程,相比較 oop 面向對象編程來講,Aop關注的再也不是程序代碼中某個類,某些方法,而aop考慮的更多的是一種面到面的切入,即層與層之間的一種切入,因此稱之爲切面。聯想你們吃的漢堡(中間夾肉)。那麼aop是怎麼作到攔截整個面的功能呢?考慮前面學到的servlet filter /* 的配置 ,實際上也是aop 的實現。日誌

AOP能作什麼?

​ AOP主要應用於日誌記錄,性能統計,安全控制,事務處理等方面,實現公共功能性的重複使用。

AOP的特色

​ 1. 下降模塊與模塊之間的耦合度,提升業務代碼的聚合度。(高內聚低耦合)

​ 2. 提升了代碼的複用性。

​ 3. 提升系統的擴展性。(高版本兼容低版本)

​ 4. 能夠在不影響原有的功能基礎上添加新的功能

AOP的底層實現

​ 動態代理(JDK + CGLIB)

AOP基本概念

Joinpoint(鏈接點)

​ 被攔截到的每一個點,spring中指被攔截到的每個方法,spring aop一個鏈接點即表明一個方法的執行。

Pointcut(切入點)

​ 對鏈接點進行攔截的定義(匹配規則定義 規定攔截哪些方法,對哪些方法進行處理),spring 有專門的表達式語言定義。

Advice(通知)

​ 攔截到每個鏈接點即(每個方法)後所要作的操做

  1. 前置通知 (前置加強)— before() 執行方法前通知
  2. 返回通知(返回加強)— afterReturn 方法正常結束返回後的通知
  3. 異常拋出通知(異常拋出加強)— afetrThrow()
  4. 最終通知 — after 不管方法是否發生異常,均會執行該通知。
  5. 環繞通知 — around 包圍一個鏈接點(join point)的通知,如方法調用。這是最強大的一種通知類型。 環繞通知能夠在方法調用先後完成自定義的行爲。它也會選擇是否繼續執行鏈接點或直接返回它們本身的返回值或拋出異常來結束執行。

Aspect(切面)

​ 切入點與通知的結合,決定了切面的定義,切入點定義了要攔截哪些類的哪些方法,通知則定義了攔截過方法後要作什麼,切面則是橫切關注點的抽象,與類類似,類是對物體特徵的抽象,切面則是橫切關注點抽象。

Target(目標對象)

​ 被代理的目標對象

Weave(織入)

​ 將切面應用到目標對象並生成代理對象的這個過程即爲織入

Introduction(引入)

​ 在不修改原有應用程序代碼的狀況下,在程序運行期爲類動態添加方法或者字段的過程稱爲引入

Spring AOP的實現

Spring AOP環境搭建

座標依賴引入

<!--Spring AOP-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

添加spring.xml的配置

添加命名空間

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd

註解實現

定義切面

/**
 * 切面
 *  切入點和通知的抽象   (與面向對象中的 類 類似)
 *  定義 切入點和通知 (切入點定義了要攔截哪些類的哪些方法,通知則定義了攔截過方法後要作什麼)
 */
@Component // 將對象交給IOC容器去實例化
@Aspect // 聲明當前類是一個切面
public class LogCut {

    /**
     *  切入點:
     *      匹配規則。規定什麼方法被攔截、須要處理什麼方法
     *  定義切入點
     *    @Pointcut("匹配規則")
     *
     *    Aop 切入點表達式簡介
     *       1. 執行任意公共方法:
     *          execution(public *(..))
     *       2. 執行任意的set方法
     *          execution(* set*(..))
     *       3. 執行com.xxxx.service包下任意類的任意方法
     *          execution(* com.xxxx.service.*.*(..))
     *       4. 執行com.xxxx.service 包 以及子包下任意類的任意方法
     *          execution(* com.xxxx.service..*.*(..))
     *
     *       注:表達式中的第一個* 表明的是方法的修飾範圍
     *          可選值:private、protected、public (* 表示全部範圍)     
     */
    @Pointcut("execution (* com.xxxx.service..*.*(..) )")
    public void cut(){}

    /**
     * 聲明前置通知   並將通知應用到定義的切入點上
     * 目標類方法執行前 執行該通知
     *
     */
   @Before(value = "cut()")
    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 聲明返回通知   並將通知應用到定義的切入點上
     * 目標類方法(無異常)執行後 執行該通知
     *
     */
   @AfterReturning(value = "cut()")
    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 聲明最終通知   並將通知應用到定義的切入點上
     * 目標類方法(無異常或有異常)執行後 執行該通知
     *
     */
    @After(value = "cut()")
    public void after() {
        System.out.println("最終通知.....");
    }

    /**
     * 聲明異常通知   並將通知應用到定義的切入點上
     * 目標類方法出現異常時 執行該通知
     */
    @AfterThrowing(value="cut()",throwing = "e")
    public void afterThrow(Exception e) {
        System.out.println("異常通知....." + "  異常緣由:" + e.getCause());
    }

    /**
     *  聲明環繞通知  並將通知應用到切入點上
     *  方法執行先後  經過環繞通知定義相應處理
     *      須要經過顯式調用對應的方法,不然沒法訪問指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */
    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知...");
        }
        System.out.println("最終通知...");

        return object;
    }

}

配置文件(spring.xml)

<!--配置AOP代理-->
 <aop:aspectj-autoproxy/>

XML實現

定義切面

**
 * 切面
 *  切入點和通知的抽象   (與面向對象中的 類 類似)
 *  定義 切入點和通知 (切入點定義了要攔截哪些類的哪些方法,通知則定義了攔截過方法後要作什麼)
 */
@Component // 將對象交給IOC容器去實例化
public class LogCut02 {

    public void cut(){}

    /**
     * 聲明前置通知   並將通知應用到定義的切入點上
     * 目標類方法執行前 執行該通知
     *
     */

    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 聲明返回通知   並將通知應用到定義的切入點上
     * 目標類方法(無異常)執行後 執行該通知
     *
     */

    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 聲明最終通知   並將通知應用到定義的切入點上
     * 目標類方法(無異常或有異常)執行後 執行該通知
     *
     */

    public void after() {
        System.out.println("最終通知.....");
    }

    /**
     * 聲明異常通知   並將通知應用到定義的切入點上
     * 目標類方法出現異常時 執行該通知
     */

    public void afterThrow(Exception e) {
        System.out.println("異常通知....." + "  異常緣由:" + e.getCause());
    }

    /**
     *  聲明環繞通知  並將通知應用到切入點上
     *  方法執行先後  經過環繞通知定義相應處理
     *      須要經過顯式調用對應的方法,不然沒法訪問指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */

    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知...");
        }
        System.out.println("最終通知...");

        return object;
    }

}

配置文件(spring.xml)

<!--aop相關配置-->
<aop:config>
    <!--aop切面-->
    <aop:aspect ref="logCut02">
        <!-- 定義aop 切入點 -->
        <aop:pointcut id="cut" expression="execution(* com.xxxx.service..*.*(..))"/>
        <!-- 配置前置通知 指定前置通知方法名  並引用切入點定義 -->
        <aop:before method="before" pointcut-ref="cut"/>
        <!-- 配置返回通知 指定返回通知方法名  並引用切入點定義 -->
        <aop:after-returning method="afterReturn" pointcut-ref="cut"/>
        <!-- 配置異常通知  指定異常通知方法名  並引用切入點定義 -->
        <aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="cut"/>
        <!-- 配置最終通知  指定最終通知方法名  並引用切入點定義 -->
        <aop:after method="after" pointcut-ref="cut"/>
        <!-- 配置環繞通知  指定環繞通知方法名  並引用切入點定義 -->
        <aop:around method="around" pointcut-ref="cut"/>
    </aop:aspect>
</aop:config>

Spring AOP總結

代理模式實現三要素

  1. 接口定義
  2. 目標對象與代理對象必須實現統一接口
  3. 代理對象持有目標對象的引用 加強目標對象行爲

代理模式實現分類以及對應區別

  1. 靜態代理:手動爲目標對象製做代理對象,即在程序編譯階段完成代理對象的建立

  2. 動態代理:在程序運行期動態建立目標對象對應代理對象。

  3. jdk動態代理:被代理目標對象必須實現某一或某一組接口 實現方式 經過回調建立代理對象。

  4. cglib 動態代理:被代理目標對象能夠沒必要實現接口,繼承的方式實現。

    動態代理相比較靜態代理,提升開發效率,能夠批量化建立代理,提升代碼複用率。

Aop 理解

  1. 面向切面,相比oop 關注的是代碼中的層 或面
  2. 解耦,提升系統擴展性
  3. 提升代碼複用

Aop 關鍵詞

  1. 鏈接點:每個方法
  2. 切入點:匹配的方法集合
  3. 切面:鏈接點與切入點的集合決定了切面,橫切關注點的抽象
  4. 通知:幾種通知
  5. 目標對象:被代理對象
  6. 織入:程序運行期將切面應用到目標對象 並生成代理對象的過程
  7. 引入:在不修改原始代碼狀況下,在程序運行期爲程序動態引入方法或字段的過程

image

相關文章
相關標籤/搜索