91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行Java代理設計模式的靜態代理和動態代理實現

發布時間:2021-12-30 17:36:49 來源:億速云 閱讀:112 作者:柒染 欄目:編程語言

今天就跟大家聊聊有關如何進行Java代理設計模式的靜態代理和動態代理實現,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

面試問題:Java里的代理設計模式一共有幾種實現方式?這個題目很像孔乙己問“茴香豆的茴字有哪幾種寫法?”

所謂代理模式,是指客戶端(Client)并不直接調用實際的對象(下圖右下角的RealSubject),而是通過調用代理(Proxy),來間接的調用實際的對象。

代理模式的使用場合,一般是由于客戶端不想直接訪問實際對象,或者訪問實際的對象存在技術上的障礙,因而通過代理對象作為橋梁,來完成間接訪問。

如何進行Java代理設計模式的靜態代理和動態代理實現

實現方式一:靜態代理

開發一個接口IDeveloper,該接口包含一個方法writeCode,寫代碼。

public interface IDeveloper {     public void writeCode();
}

創建一個Developer類,實現該接口。

public class Developer implements IDeveloper{    private String name;    public Developer(String name){        this.name = name;
    }    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes code");
    }
}

測試代碼:創建一個Developer實例,名叫Jerry,去寫代碼!

public class DeveloperTest {    public static void main(String[] args) {
        IDeveloper jerry = new Developer("Jerry");
        jerry.writeCode();
    }
}

現在問題來了。Jerry的項目經理對Jerry光寫代碼,而不維護任何的文檔很不滿。假設哪天Jerry休假去了,其他的程序員來接替Jerry的工作,對著陌生的代碼一臉問號。經全組討論決定,每個開發人員寫代碼時,必須同步更新文檔。

為了強迫每個程序員在開發時記著寫文檔,而又不影響大家寫代碼這個動作本身, 我們不修改原來的Developer類,而是創建了一個新的類,同樣實現IDeveloper接口。這個新類DeveloperProxy內部維護了一個成員變量,指向原始的IDeveloper實例:

public class DeveloperProxy implements IDeveloper{    private IDeveloper developer;    public DeveloperProxy(IDeveloper developer){        this.developer = developer;
    }    @Override
    public void writeCode() {
        System.out.println("Write documentation...");        this.developer.writeCode();
    }
}

這個代理類實現的writeCode方法里,在調用實際程序員writeCode方法之前,加上一個寫文檔的調用,這樣就確保了程序員寫代碼時都伴隨著文檔更新。

測試代碼:

如何進行Java代理設計模式的靜態代理和動態代理實現

靜態代理方式的優點

1. 易于理解和實現

2. 代理類和真實類的關系是編譯期靜態決定的,和下文馬上要介紹的動態代理比較起來,執行時沒有任何額外開銷。

靜態代理方式的缺點

每一個真實類都需要一個創建新的代理類。還是以上述文檔更新為例,假設老板對測試工程師也提出了新的要求,讓測試工程師每次測出bug時,也要及時更新對應的測試文檔。那么采用靜態代理的方式,測試工程師的實現類ITester也得創建一個對應的ITesterProxy類。

public interface ITester {    public void doTesting();
}
Original tester implementation class:public class Tester implements ITester {    private String name;    public Tester(String name){        this.name = name;
    }    @Override
    public void doTesting() {
        System.out.println("Tester " + name + " is testing code");
    }
}public class TesterProxy implements ITester{    private ITester tester;    public TesterProxy(ITester tester){        this.tester = tester;
    }    @Override
    public void doTesting() {
        System.out.println("Tester is preparing test documentation...");
        tester.doTesting();
    }
}

正是因為有了靜態代碼方式的這個缺點,才誕生了Java的動態代理實現方式。

Java動態代理實現方式一:InvocationHandler

InvocationHandler的原理我曾經專門寫文章介紹過: Java動態代理之InvocationHandler最簡單的入門教程

通過InvocationHandler, 我可以用一個EnginnerProxy代理類來同時代理Developer和Tester的行為。

public class EnginnerProxy implements InvocationHandler {
    Object obj;    public Object bind(Object obj)
    {        this.obj = obj;        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);
    }    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable    {
        System.out.println("Enginner writes document");
        Object res = method.invoke(obj, args);        return res;
    }
}

真實類的writeCode和doTesting方法在動態代理類里通過反射的方式進行執行。

測試輸出:

如何進行Java代理設計模式的靜態代理和動態代理實現

通過InvocationHandler實現動態代理的局限性

假設有個產品經理類(ProductOwner) 沒有實現任何接口。

public class ProductOwner {    private String name;    public ProductOwner(String name){        this.name = name;
    }    public void defineBackLog(){
        System.out.println("PO: " + name + " defines Backlog.");
    }
}

我們仍然采取EnginnerProxy代理類去代理它,編譯時不會出錯。運行時會發生什么事?

ProductOwner po = new ProductOwner("Ross");
ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po);
poProxy.defineBackLog();

運行時報錯。所以局限性就是:如果被代理的類未實現任何接口,那么不能采用通過InvocationHandler動態代理的方式去代理它的行為。

如何進行Java代理設計模式的靜態代理和動態代理實現

Java動態代理實現方式二:CGLIB

CGLIB是一個Java字節碼生成庫,提供了易用的API對Java字節碼進行創建和修改。關于這個開源庫的更多細節,請移步至CGLIB在github上的倉庫: https://github.com/cglib/cglib

我們現在嘗試用CGLIB來代理之前采用InvocationHandler沒有成功代理的ProductOwner類(該類未實現任何接口)。

現在我改為使用CGLIB API來創建代理類:

public class EnginnerCGLibProxy {
    Object obj;    public Object bind(final Object target)
    {        this.obj = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new MethodInterceptor() {            @Override
            public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable            {
                System.out.println("Enginner 2 writes document");
                Object res = method.invoke(target, args);                return res;
            }
        }
        );        return enhancer.create();
    }
}

測試代碼:

ProductOwner ross = new ProductOwner("Ross");
ProductOwner rossProxy = (ProductOwner) new EnginnerCGLibProxy().bind(ross);
rossProxy.defineBackLog();

盡管ProductOwner未實現任何代碼,但它也成功被代理了:

如何進行Java代理設計模式的靜態代理和動態代理實現

用CGLIB實現Java動態代理的局限性

如果我們了解了CGLIB創建代理類的原理,那么其局限性也就一目了然。我們現在做個實驗,將ProductOwner類加上final修飾符,使其不可被繼承:

如何進行Java代理設計模式的靜態代理和動態代理實現

再次執行測試代碼,這次就報錯了: Cannot subclass final class XXXX。

所以通過CGLIB成功創建的動態代理,實際是被代理類的一個子類。那么如果被代理類被標記成final,也就無法通過CGLIB去創建動態代理。

Java動態代理實現方式三:通過編譯期提供的API動態創建代理類

假設我們確實需要給一個既是final,又未實現任何接口的ProductOwner類創建動態代碼。除了InvocationHandler和CGLIB外,我們還有最后一招:

我直接把一個代理類的源代碼用字符串拼出來,然后基于這個字符串調用JDK的Compiler(編譯期)API,動態的創建一個新的.java文件,然后動態編譯這個.java文件,這樣也能得到一個新的代理類。

如何進行Java代理設計模式的靜態代理和動態代理實現

測試成功:

如何進行Java代理設計模式的靜態代理和動態代理實現

我拼好了代碼類的源代碼,動態創建了代理類的.java文件,能夠在Eclipse里打開這個用代碼創建的.java文件,

如何進行Java代理設計模式的靜態代理和動態代理實現

如何進行Java代理設計模式的靜態代理和動態代理實現

下圖是如何動態創建ProductPwnerSCProxy.java文件:

如何進行Java代理設計模式的靜態代理和動態代理實現

下圖是如何用JavaCompiler API動態編譯前一步動態創建出的.java文件,生成.class文件:

如何進行Java代理設計模式的靜態代理和動態代理實現

下圖是如何用類加載器加載編譯好的.class文件到內存:

如何進行Java代理設計模式的靜態代理和動態代理實現

看完上述內容,你們對如何進行Java代理設計模式的靜態代理和動態代理實現有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

得荣县| 渝中区| 晋州市| 山东省| 内江市| 镶黄旗| 石河子市| 杂多县| 观塘区| 灌云县| 长沙县| 高淳县| 宁陕县| 鄄城县| 清新县| 通江县| 华池县| 彰化县| 亳州市| 芮城县| 盖州市| 桦南县| 天津市| 宁河县| 大英县| 利辛县| 伊通| 宜城市| 嘉定区| 司法| 黔西县| 铁力市| 西畴县| 航空| 营山县| 上饶市| 夏河县| 宁安市| 凤凰县| 大埔区| 木兰县|