【Spring】Spring複習第三天

一、AOP的相關概念

1.1 AOP概述

1.1.1 什麼是AOP?

AOP:全稱是Aspect Oriented Programming 即:面向切面編程。
在軟件業,AOP通過預編譯的方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架的一個重要內容,是函數式編程的一種衍生泛型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

簡單地說,它就是把我們程序重複的代碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改源碼的基礎上,對我們已有方法進行增強。

1.2 AOP的作用及優勢

  • 作用:在程序運行期間,不修改源碼對已有方法進行增強。
  • 優勢:
    • 減少重複代碼
    • 提高開發效率
    • 維護方便

1.3 AOP的實現方式

使用動態代理技術

2. AOP的具體應用

2.1 一些問題

我們在業務層中實現事務控制的時候,業務層的方法中存在着很多的重複代碼。並且業務層方法和事務控制方法中耦合了。我們可以使用下面的知識來解決這些問題。

2.2 動態代理回顧

2.2.1 動態代理的特點

字節碼隨用隨創建,隨用隨加載。
他與靜態代理的區別也在於此。因爲靜態代理是一上來字節碼就創建好,並加載完成。

2.2.2 動態代理常用的兩種方式

  • 基於接口的動態代理
    • 提供者:JDK官方的Proxy類。
    • 要求:被代理類最少實現一個接口。
  • 基於子類的動態代理
    • 提供者:第三方的CGLib,如果報asmxxxx異常,需要導入asm.jar。
    • 要求:被代理類不能用final修飾的類。

2.2.3 使用JDK官方的Proxy 類創建代理對象

/**
 * 一個經紀公司的要求:
 * 能做基本的表演和危險的表演
 */

public interface IActor {
    /**
     * 基本的演出
     *
     */
    public void basicAct(float money);

    /**
     * 危險演出
     */
    public void dangerAct(float money);
}

public class Actor implements IActor {

    /**
     * 基本演出
     * @param money
     */
    @Override
    public void basicAct(float money) {
        System.out.println("拿到錢,開始基本的表演:"+money);
    }

    /**
     * 危險演出
     * @param money
     */
    @Override
    public void dangerAct(float money) {
        System.out.println("拿到錢,開始危險的表演:"+money);
    }
}
public class Client {
    public static void main(String[] args) {
        //一個劇組找演員
        Actor actor = new Actor();//直接

        /**
         * 代理:間接。
         * 獲取代理對象:
         *  要求:被代理類最少實現一個接口。
         * 創建的方式:Proxy.newProxyInstance(三個參數)
         * 參數含義:
         * ClassLoader:和被代理對象使用相同的類加載器。
         * Interfaces:和被代理對象具有相同的行爲。實現相同的接口。
         * InvocationHandler:如何代理。
         *      策略模式:使用場景是:
         *          數據有了,目的明確。
         *          達成目標的過程,就是策略。
         */
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 執行被代理對象的任何方法,都會經過該方法。
                 * 此方法有攔截的功能。
                 *  參數:
                 *      proxy:代理對象的引用。不一定每次都用得到。
                 *      method:當前執行的方法對象。
                 *      args:執行方法所需的參數。
                 *  返回值:
                 *      當前執行方法的返回值。
                 */
                String name = method.getName();
                Float money = (Float)args[0];
                Object rtValue = null;
                //每個經紀公司對不同演出收費不一樣,此處開始判斷
                if ("basicAct".equals(name)) {
                    //基本演出
                    if (money > 2000) {
                        //看上去劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                if ("dangerAct".equals(name)) {
                    //基本演出
                    if (money > 5000) {
                        //看上去898劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                return rtValue;
            }
        });
        //沒有經濟公司的時候,直接找演員。
        actor.basicAct(100f);
        actor.dangerAct(500f);

        //劇組無法直接聯繫演員,而是由經紀公司找的演員
        proxyActor.basicAct(8000f);
        proxyActor.dangerAct(50000f);
    }
}

2.2.3 使用CGLib的Enhancer類創建代理對象

public class Actor {
    /**
     * 基本演出
     * @param money
     */
    public void basicAct(float money) {
        System.out.println("CGLIB拿到錢,開始基本的表演:"+money);
    }

    /**
     * 危險演出
     * @param money
     */
    public void dangerAct(float money) {
        System.out.println("CGLIB拿到錢,開始危險的表演:"+money);
    }
}

public class Client {
    public static void main(String[] args) {
        //一個劇組找演員
        final Actor actor = new Actor();//直接(注意在匿名內部類中只能引用final類型的外部變量)

        /**
         * 基於子類的動態代理
         *  要求:被代理的對象不能是最終類。
         *  用到的類:Enhancer
         *  用到的方法:create(Class,Callback)
         *  方法的參數:
         *      Class:被代理對象的字節碼。
         *      Callback:如何代理。
         */
        Actor cglibActor = (Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
            /**
             * 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何方法進行增強。
             *
             * 參數:
             *      前三個和基於接口的動態代理是一樣的。
             *      MethodProxy:當前執行方法的代理對象。
             *      返回值:當前執行方法的返回值。
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                String name = method.getName();
                Float money = (Float) args[0];
                Object rtValue = null;
                //每個經紀公司對不同演出收費不一樣,此處開始判斷
                if ("basicAct".equals(name)) {
                    //基本演出
                    if (money > 2000) {
                        //看上去劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                if ("dangerAct".equals(name)) {
                    //基本演出
                    if (money > 5000) {
                        //看上去898劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                return rtValue;
            }
        });**加粗樣式**
        cglibActor.basicAct(10000);
        cglibActor.dangerAct(100000);
    }
}

二、Spring中的AOP

1. Spring中AOP的細節

1.1 AOP相關術語

  • JoinPoint(連接點):所謂連接點是指那些被攔截到的點。在Spring中,這些點指的是方法,因爲Spring只支持方法類型的連接點。
  • PointCut(切入點):是指我們要對哪些JoinPoint 進行攔截的定義。
  • Advice(通知/增強):所謂通知是指攔截到JoinPoint之後所要做的事情就是通知(增強的方法抽取而形成的類)
    通知的類型:前置通知、後置通知、異常通知、最終通知、環繞通知。
  • Introduction(引介):引介是一種特殊的通知在不修改類代碼的前提下,Introduction可以在運行期爲類動態的添加一些方法或者Field。
  • Target(目標對象):代理的目標對象(被代理對象)。
  • Weaving(織入):是指把增強應用到目標對象來創建新的代理對象的過程。
    Spring採用動態代理織入,而AspectJ採用編譯器織入和類裝載期織入。
  • Proxy(代理):一個類被AOP織入增強後,就產生一個結果代理類。
  • Aspect(切面):是切入點和通知(引介)的結合。

1.2 學習Spring中AOP要明確的事

  • 開發階段(我們做的)

    • 編寫業務核心代碼(開發主線):大部分程序員來做,要求熟悉業務需求。
    • 公用代碼抽取出來,製作成通知。(開發階段最後在做):AOP編程人員來做。
    • 在配置文件中,聲明切入點與通知間的關係,即切面:AOP編程人員來做。
  • 運行階段(Spring框架完成的)

Spring框架監控切入點方法的執行。一旦監控到切入點方法被執行,使用代理機制,動態創建目標對象的代理對象,根據通知類別,在代理對象的對應位置,將通知對應的功能織入,完成完整的代碼邏輯運行。

1.3 關於代理的選擇

在Spring中,框架會根據目標類是否實現了接口來決定採用哪種動態代理的方式。

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