設計模式之動態代理(spring、cglib、 jdkproxy)

靜態代理與動態代理的區別:

靜態代理的目標對象比較單一 是某一類或實現同一接口的實例,不利於擴展,而動態代理的目標對象就沒有限制,目標對象可以任意實例,如果目標對象實現了接口則可以使用jdk自帶的代理類實現,如果沒有實現接口則可以使用第三方的組件Cglib來實現。

動態代理

1) 先創建一個通用接口

package com.arno.summary.proxy.dynamicproxy.jdkproxy;

/**
 * Created by Arno
 */
public interface IPerson {
    void findLover();
}

2)實現該接口的兩個實現類

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
 * Created by Arno
 */
public class Person1 implements IPerson {
    public void findLover() {
        System.out.println("Person1要求:有車有房學歷高");
    }
}

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
 * Created by Arno
 */
public class Person2 implements IPerson {
    public void findLover() {
        System.out.println("Person2要求:膚白貌美大長腿");
    }
}

3)創建一個代理類,實現java.lang.reflect.InvocationHandler,通過invoke來反射來生成有目標類的實例引用的代理class。

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Created by Arno
 */
public class JdkMeipo implements InvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson target){
        this.target = target;
        Class<?> clazz =  target.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after() {
        System.out.println("雙方同意,開始交往");
    }

    private void before() {
        System.out.println("我是媒婆,已經收集到你的需求,開始物色");
    }
}

4)開始測試

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
 * Created by Arno
 */
public class Test {
    public static void main(String[] args) {
        JdkMeipo jdkMeipo = new JdkMeipo();
        IPerson person1 = jdkMeipo.getInstance(new Person1());
        person1.findLover();

        IPerson person2 = jdkMeipo.getInstance(new Person2());
        person2.findLover();

    }
}

5)運行結果

我是媒婆,已經收集到你的需求,開始物色
Person1要求:有車有房學歷高
雙方同意,開始交往
我是媒婆,已經收集到你的需求,開始物色
Person2要求:膚白貌美大長腿
雙方同意,開始交往

6)分析
要想使用jdk原生的代理,必須實現接口InvocationHandler;且被代理的類都要實現接口。

靜態代理:

1)創建一個實例

package com.arno.summary.proxy.staticproxy;

/**
 * Created by Arno
 */
public class Person2  {

    public void findLover() {
        System.out.println("子女自己相處");
    }

}


2)創建第二個實例。有第一個實例的引用,且他可以通過該飲用調用第一個實例的方法

package com.arno.summary.proxy.staticproxy;

/**
 * Created by Arno
 */
public class Person1  {

    private Person2 person2;

    public Person1(Person2 person2) {
        this.person2 = person2;
    }

    public void findLover() {
        System.out.println("父母幫忙物色");
        person2.findLover();
        System.out.println("開始交往");
    }

}

3)測試

package com.arno.summary.proxy.staticproxy;

/**
 * Created by Arno
 */
public class Test {
    public static void main(String[] args) {
        Person1 person1 = new Person1(new Person2());
        person1.findLover();
    }
}

4)運行結果

父母幫忙物色
子女自己相處
開始交往

5)分析
spring中的依賴注入都是靜態代理。如果想要使用被代理的類的新增方法,一定要修改代理類裏的方法,比動態代理僵硬,只能硬編碼

cglib實現動態代理的原理及與jdk原生的動態代理的區別

1)cglib的實現
CGLib代理執行代理方法的效率之所以比JDK的高,是因爲CGlib採用了FastClass機制,它的原理簡單來說就是:爲代理類和被代理類各生成一個類,這個類會爲代理類或被代理類的方法分配index(int類型);這個index當做一個入參,FastClass就可以直接定位要調用的方法並直接進行調用歐冠,省去了反射調用,所以調用效率比JDK代理通過反射調用高。cglib無法代理final標記的方法

2)cglib與jdk的比較
a)JDK動態代理實現了被代理對象的接口,cglib代理繼承了被代理對象。
b)JDK動態代理和cglib代理都在運行期間生成字節碼,jdk動態代理直接寫class字節碼,cglib使用asm框架寫class字節碼,cglib代碼實現原理更復雜,生成代理類比jdk動態代理低
c)JDK動態代理調用代理方法是通過反射機制調用的,cglib代理是通過fastclass機制直接調用方法,cglib執行效率更高。

3)spring中的代理選擇策略
在spring當中,使用了jdk代理,也使用了cglib代理;
默認的代理選擇原則爲:
1)當bean有實現接口時,則使用jdk動態代理
2)當bean沒有實現接口時,使用cglib實現動態代理
3)spring可以通過配置強行使用cglib代理:

<aop:aspectj-autoproxy proxy-target-class="true" />

總結

1)靜態代理與動態代理的本質區別
a)靜態代理只能通過手動完成代理操作,如果被代理類增加了新的方法,代理類需要同步增加,違背開閉原則。
b)動態代理採用在運行時動態生成字節碼的方式,取消了代理類的擴展限制,遵循開閉原則。
c)若動態代理要對目標類的增強邏輯進行擴展,結合策略模式,只增強新增策略類便可完成,無需修改代理類代碼。

2)代理模式的優缺點:
優點
a)代理模式能將代理對象與真實被調用目標對象隔離
b)在一定程度上降低了系統的耦合性,擴展性好
c)可以起到保護目標對象作用
d)可以增強目標對象的功能
缺點
a)代理模式會造成系統設計中類的數量增加
b)在客戶端與目標對象中增加一個代理對象,會導致請求處理速度變慢
c)增加系統的複雜度

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