深刻理解JAVA中的代理模式

前言java

代理是什麼spring

事故現場:我家的寵物今天生病了,而我又沒有相關的醫學知識,因此我只讓個人寵物多喝熱水嗎?ide

結果顯然是不行的,因此我須要去找寵物醫生這些更專業的人來幫個人寵物治病。學習

這個時候,代理就出現了,而寵物醫生就是代理,而我就是目標對象。測試

總結起來就是代理代替目標對象執行相關操做,便是對目標對象的一種功能擴展。this

使用代理模式的條件spa

一、兩個角色:執行者,被代理對象代理

二、注重過程,必需要作,被代理的對象沒時間作或者不想作,不專業code

三、執行者必須拿到被代理對象的我的資料對象

1.靜態代理

代碼實現:

/**
 * 我和寵物醫生都是人,都有治療技能,可是寵物醫生比我更專業
 */
interface IPerson{
    void treat(Pet pet); //治療技能
}
/**
 * 寵物類
 */
class Pet{
    private String name;
    public Pet(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

/**
 * 目標對象實現」IPerson「接口
 */
class Self implements IPerson{
    private Pet pet;
    public Self(Pet pet){
        this.pet = pet;
    }
    public void treat(Pet pet){
        System.out.println(pet.getName() + ",你要多喝點水");
    }
}

/**
 * 代理對象與目標對象實現同一接口
 */
class PetDoctor implements IPerson{
    //接收目標對象
    private IPerson targetObj;
    public PetDoctor(IPerson targetObj){
        this.targetObj = targetObj;
    }
    @Override
    public void treat(Pet pet) {
        System.out.println("對" + pet.getName() + "進行檢查");
        targetObj.treat(pet);
        System.out.println("對" + pet.getName() + "進行治療");
    }
}

代碼測試:

public static void main(String[] args){
        //個人寵物
        Pet pet = new Pet("多多");
        //目標對象
        IPerson target = new Self(pet);
        //代理對象
        IPerson proxy = new PetDoctor(target);
        proxy.treat(pet);
    }

運行結果:

寵物醫生對多多進行檢查
我對多多說,你要多喝點水
寵物醫生對多多進行治療

結果很明顯,醫生比我更專業,我只會讓個人寵物喝水,但醫生會先檢查再進行專業的治療,因此說代理是讓更專業的對象幫你作事。

2.動態代理

動態代理又分爲jdk動態代理和cglib動態代理,二者的區別是jdk動態代理的實現是基於接口,而cglib動態代理是基於繼承,但二者作的是同一件事,那就是字節碼重組

基本流程都是根據目標對象的資料,經過反射獲取該對象的信息,而後根據信息按照特定的寫法重寫一個java類,再進行編譯並動態加載到JVM中運行,因此說動態代理在底層其實就是實現了字節碼重組。

 jdk動態代理實例演示

 Person接口

//定義Person接口,技能是煮飯
public interface Person {
    void cook();
}

我本身,也就是被代理的對象,但我只會作可樂雞翅

public class Oneself implements Person {
    @Override
    public void cook() {
        System.out.println("我會作可樂雞翅");
    }
}

動態代理類,也是一個廚師,由於初始對於作菜比我更專業

public class Kitchener implements InvocationHandler{
    //須要代理的目標對象
    private Object object;

    public Kitchener(Object object){
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我會作糖醋排骨");
        method.invoke(object,args);    //這是我會作的,其他兩樣是代理對象初始會作的
        System.out.println("我會作九轉大腸");
        return null;
    }
}

測試代碼

public class TestJdk {
    public static void main(String[] args){
        //建立目標代理對象
        Oneself oneself = new Oneself();

        InvocationHandler kitchener = new Kitchener(oneself);
        /*
         * 經過Proxy的newProxyInstance方法來建立咱們的代理對象,作的就是字節碼重組的工做,新生成一個java類在編譯再加載到JVM運行
         * 第一個參數是類加載器
         * 第二個參數是咱們這裏爲代理對象提供的接口,也就是代理對象所實現的接口,因此說在jdk動態代理中被代理對象須要實現一個接口
         * 第三個參數handler, 咱們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
         */
        Person proxy = (Person) Proxy.newProxyInstance(kitchener.getClass().getClassLoader(),
                                                        oneself.getClass().getInterfaces(),
                                                        kitchener);
        System.out.println(proxy.getClass());  // (1)
        proxy.cook();

    }
}

測試結果

class com.sun.proxy.$Proxy0    //(2)
我會作糖醋排骨
我會作可樂雞翅
我會作九轉大腸

能夠看到(1)行代碼打印出來的是一個代理類,而代理對象經過生成java類再編譯加載運行對用戶來講是無感知的,咱們只知道返回回來的是一個代理對象,而後由代理對象去幫咱們作事。

而cglib代理的實現原理也是同樣的,只不過一個是基於接口,一個是基於繼承,原理都是經過反射獲取對象信息再根據對象信息建立java類編譯加載運行,因此cglib暫時就不展開了,後期能夠本身手寫一個動態加深理解。

學習了動態代理後,在本人的工做中是沒使用過的,但倒是瞭解spring的AOP實現的必要基礎,由於spring的AOP實現就是基於動態代理實現的。

相關文章
相關標籤/搜索