Java常用設計模式之代理模式

一:代理模式概念

所以說代理模式就是:當前對象不願意乾的,沒法乾的東西委託給別的對象來做,我只要做好本分的東西就好了!

什麼情況下使用呢:

其實只要記住一點:原有的對象需要額外的功能,想想動態代理這項技術

下面就以,程序員寫代碼爲例來說。

二:代碼描述代理模式

1:靜態代理模式

(1):一個接口,程序員每天寫代碼

public interface Programmer {
    void coding();
}

(2):JavaProgrammer 也是一個程序員,他也寫代碼(每個程序員寫的代碼都不一樣,所以分了接口和實現類)

public class JavaProgrammer implements Programmer {
    @Override
    public void coding() {
        System.out.println("Java程序員最新文章:。。。。。");
    }
}

此時JavaProgrammer已經是一個網紅了,他不想枯燥地寫代碼。他在想:“在寫代碼時能賺錢就好咯,有人給我錢,我才寫代碼”。但是,JavaProgrammer的文筆太爛了,一旦有什麼冬瓜豆腐,分分鐘變成過氣網紅,這是JavaProgrammer不願意看到的。

(3): 所以JavaProgrammer去請了一個程序員大V(代理)來實現自己的計劃,這個程序員大V會每次讓JavaProgrammer發文章時,就給JavaProgrammer點贊、評論、鼓吹這文章好。只要流量有了,錢就到手了。

public class ProgrammerBigV implements Programmer {

    private JavaProgrammer javaProgrammer;
    public ProgrammerBigV(JavaProgrammer javaProgrammer){
        this.javaProgrammer=javaProgrammer;
    }

    public ProgrammerBigV(){
        this.javaProgrammer=new JavaProgrammer();
    }

  

    public void upvote(){
        System.out.println("點贊");
    }
    @Override
    public void coding() {
        javaProgrammer.coding();
        upvote();
    }
}

文章(代碼)還是由JavaProgrammer來發,但每次發送之後程序員大V都會點贊。

(4):測試下

public static void main(String[] args1){
        avaProgrammer javaProgrammer=new JavaProgrammer();
        Programmer programmer=new ProgrammerBigV(javaProgrammer);
        programmer.coding();

    }

這樣一來,不明真相的路人就覺得JavaProgrammer是真厲害,這就是知識付費。

此時,真實對象(JavaProgrammer)對外界來說是透明的

2:代理自定義方法

程序員大V看到JavaProgrammer一直順風順水,賺大錢了。覺得是時候要加價了,於是在點贊完畢後就跟JavaProgrammer說每點完一次贊加100塊

於是乎,程序員大V就增添了另外一個方法:addMoney()

public class ProgrammerBigV implements Programmer {

    private JavaProgrammer javaProgrammer;
    public ProgrammerBigV(JavaProgrammer javaProgrammer){
        this.javaProgrammer=javaProgrammer;
    }

    public ProgrammerBigV(){
        this.javaProgrammer=new JavaProgrammer();
    }

    // 加價啦
    public void addMoney() {
        System.out.println("這次我要加100塊");
    }

    public void upvote(){
        System.out.println("點贊");
    }
    @Override
    public void coding() {
        javaProgrammer.coding();
        upvote();
        addMoney();
    }
}

於是乎程序員大V每次都能多100塊

3:動態代理

幾年時間過去了,JavaProgrammer靠着程序員的大V點贊還是沒發財(本質上JavaProgrammer還沒有乾貨,沒受到大衆的認可)。此時已經有很多人晉升成了程序員大V了,但是之前的那個程序員大V還是一直累加着錢…雖然在開始的時候JavaProgrammer嚐到了甜頭,但現在JavaProgrammer財政已經匱乏了。

JavaProgrammer將自己的失敗認爲:一定是那個程序員大V轉門爲我點贊被識破了,吃瓜羣衆都知道了,他收費又那麼貴。

於是JavaProgrammer不請程序員大V了,請水軍來點贊(水軍便宜,只要能點贊就行了):

public static void main(String[] args1){
     
        // Java請水軍
       JavaProgrammer java = new JavaProgrammer();

        Programmer programmerWaterArmy = (Programmer) Proxy.newProxyInstance(java.getClass().getClassLoader(),
                java.getClass().getInterfaces(),
                (proxy, method, args) -> {

            // 如果是調用coding方法,那麼水軍就要點讚了
            if (method.getName().equals("coding")) {
                method.invoke(java, args);
                System.out.println("我是水軍,我來點讚了!");

            } else {
                // 如果不是調用coding方法,那麼調用原對象的方法
                return method.invoke(java, args);
            }

            return null;
        });

        // 每當java寫完文章,水軍都會點贊
        programmerWaterArmy.coding();

    }

說明:Java提供了一個Proxy類,調用它的newInstance方法可以生成某個對象的代理對象,該方法需要三個參數:

  • 參數一:生成代理對象使用哪個類裝載器【一般我們使用的是被代理類的裝載器】

  • 參數二:生成哪個對象的代理對象,通過接口指定【指定要被代理類的接口】

  • 參數三:生成的代理對象的方法裏幹什麼事【實現handler接口,我們想怎麼實現就怎麼實現】

 

在編寫動態代理之前,要明確幾個概念:

  • 代理對象擁有目標對象相同的方法【因爲參數二指定了對象的接口,代理對象會實現接口的所有方法】

  • 用戶調用代理對象的什麼方法,都是在調用處理器的invoke方法。【被攔截】

  • 使用JDK動態代理必須要有接口【參數二需要接口】

上面也說了:代理對象會實現接口的所有方法,這些實現的方法交由我們的handler來處理!

  • 所有通過動態代理實現的方法全部通過invoke()調用

4:靜態代理和動態代理的區別

很明顯的是:

  • 靜態代理需要自己寫代理類-->代理類需要實現與目標對象相同的接口

  • 而動態代理不需要自己編寫代理類--->(是動態生成的)

使用靜態代理時:

  • 如果目標對象的接口有很多方法的話,那我們還是得一一實現,這樣就會比較麻煩

使用動態代理時:

  • 代理對象的生成,是利用JDKAPI,動態地在內存中構建代理對象(需要我們指定創建 代理對象/目標對象 實現的接口的類型),並且會默認實現接口的全部方法

三:Spring中AOP的代理模式

AOP(Aspect-Oriented Programming:面向切面編程)能夠將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任(例如事務處理、日誌管理、權限控制等)封裝起來,便於減少系統的重複代碼降低模塊間的耦合度,並有利於未來的可拓展性和可維護性

Spring AOP 就是基於動態代理的,如果要代理的對象,實現了某個接口,那麼Spring AOP會使用JDK Proxy,去創建代理對象,而對於沒有實現接口的對象,就無法使用 JDK Proxy 去進行代理了,這時候Spring AOP會使用Cglib ,這時候Spring AOP會使用 Cglib 生成一個被代理對象的子類來作爲代理。

當然你也可以使用 AspectJ ,Spring AOP 已經集成了AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。

使用 AOP 之後我們可以把一些通用功能抽象出來,在需要用到的地方直接使用即可,這樣大大簡化了代碼量。我們需要增加新功能時也方便,這樣也提高了系統擴展性。日誌功能、事務管理等等場景都用到了 AOP 。

Spring AOP 和 AspectJ AOP 有什麼區別?

Spring AOP 屬於運行時增強,而 AspectJ 是編譯時增強。 Spring AOP 基於代理(Proxying),而 AspectJ 基於字節碼操作(Bytecode Manipulation)。

Spring AOP 已經集成了 AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。AspectJ 相比於 Spring AOP 功能更加強大,但是 Spring AOP 相對來說更簡單,

如果我們的切面比較少,那麼兩者性能差異不大。但是,當切面太多的話,最好選擇 AspectJ ,它比Spring AOP 快很多。

 

 

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