設(shè)計模式-代理模式(Proxy Pattern)

上一篇 <<<Java基礎(chǔ)-反射機(jī)制
下一篇 >>>Java基礎(chǔ)-字節(jié)碼技術(shù)


代理模式:使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問。

  • 優(yōu)點:減少代碼冗余、提高代碼復(fù)用性、安全性、隱藏真實角色、非入侵

應(yīng)用場景

  • Spring AOP
  • 過濾器
  • 自定義注解
  • 全局捕獲異常
  • 事務(wù)原理
  • 日志收集打印
  • 權(quán)限控制
  • RPC遠(yuǎn)程調(diào)用
  • 安全代理可以隱蔽真實角色
  • Mybatis的Mapper
  • 全局ID(LCN/Seata中)

分類

靜態(tài)代理:實現(xiàn)接口和實現(xiàn)集成兩種方式
動態(tài)代理:JDK代理(接口實現(xiàn))和CGLIB代理(繼承方式)

區(qū)別:靜態(tài)代理需要自己寫代理類,而動態(tài)代理不需要寫代理類。

靜態(tài)代理

  • 接口實現(xiàn)
/**靜態(tài)代理:接口實現(xiàn)*/
OrderService orderService = new OrderServiceProxy1(new OrderServiceImpl());
orderService.order();
/** 
* 接口實現(xiàn)方式
*/
public class OrderServiceProxy1 implements OrderService {
    /**
     * 代理對象
     */
    private OrderService proxiedOrderService;

    public OrderServiceProxy1(OrderService orderService) {
      this.proxiedOrderService=orderService;
    }

    public void order() {
        System.out.println("日志收集開始..");
        proxiedOrderService.order();
        System.out.println("日志收集結(jié)束..");
    }
}
  • 繼承實現(xiàn)
/**靜態(tài)代理:繼承實現(xiàn)*/
OrderService orderService = new OrderServiceProxy();
orderService.order();
/**
 * 繼承方式
 */
public class OrderServiceProxy extends OrderServiceImpl {
    @Override
    public void order() {
        System.out.println("日志收集開始..");
        super.order();
        System.out.println("日志收集結(jié)束..");
    }
}

動態(tài)代理

1.JDK動態(tài)代理

執(zhí)行步驟
1.創(chuàng)建被代理的接口和類;
2.實現(xiàn)InvocationHandler接口,對目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理;
3.調(diào)用Proxy的靜態(tài)方法,創(chuàng)建代理類并生成相應(yīng)的代理對象;

1).核心原理 (代理類的生成、編譯及加載到j(luò)vm中)

a、生成代理類---該類實現(xiàn)了接口,并集成Proxy類【通過編譯之后可查看】

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

b、執(zhí)行代理類的order等目標(biāo)方法,實際上是調(diào)用MyInvocationHandel h的invoke方法
缺點:jdk動態(tài)代理,必須是面向接口,目標(biāo)業(yè)務(wù)類必須實現(xiàn)接口

2).調(diào)試源碼

JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = jdkInvocationHandler.getProxy();
proxy.order();
/**
 * JDK動態(tài)代理
 */
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目標(biāo)代理對象
     */
    public Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>日志收集開始>>>>");
        // 執(zhí)行代理對象方法
        Object reuslt = method.invoke(target, args);
        System.out.println(">>>日志收集結(jié)束>>>>");
        return reuslt;
    }

    /**
     * 獲取代理對象接口
     *
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

3).自動生成的代理類源碼

public final class $Proxy0 extends Proxy implements OrderService
{
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    public $Proxy0(final InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    
    public final void order() {
        try {
            super.h.invoke(this, $Proxy0.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    static {
        try {
            $Proxy0.m3 = Class.forName("cn.jarye.service.OrderService").getMethod("order", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
}

4).手寫JDK動態(tài)代理思路

a.使用java反射機(jī)制拼接$Proxy.java類的源代碼---參考Proxy.newProxyInstance方法
b.需要將$Proxy.java編譯成$Proxy.class
c.程序中直接讀取該class文件到內(nèi)存中

2.CGLIB動態(tài)代理

1).核心原理

利用asm字節(jié)碼開源包,對代理對象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理。

2).調(diào)試源碼

CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
//相當(dāng)于生成代理類
Enhancer enhancer = new Enhancer();
// 設(shè)置代理類的付類
enhancer.setSuperclass(OrderServiceImpl.class);
// 設(shè)置回調(diào)對象
enhancer.setCallback(cglibMethodInterceptor);
// 創(chuàng)建代理對象
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
orderServiceImpl.order();
public class CglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日志收集開始...>>>>>>>");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日志收集結(jié)束...>>>>>>>");
        return reuslt;
    }
}

3).自動生成的代理類源碼

  • 索引機(jī)制
    使用字節(jié)碼技術(shù)獲取當(dāng)前所有的方法,對每個方法加上一個索引,直接根據(jù)索引調(diào)用到目標(biāo)方法效率是比反射機(jī)制要高。
MethodProxy類的源碼:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
  • 索引計算方法
    使用方法名稱+參數(shù)類型計算hash值,在根據(jù)hash值得出索引
  • 代理類 (使用繼承方法)
public class OrderServiceImpl$$EnhancerByCGLIB$$279607fc extends OrderServiceImpl implements Factory
{
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$order$0$Method;
    private static final MethodProxy CGLIB$order$0$Proxy;
    
    static void CGLIB$STATICHOOK1() {
        final Class<?> forName3;
        CGLIB$order$0$Method = ReflectUtils.findMethods(new String[] { "order", "()V" }, (forName3 = Class.forName("com.jarye.service.impl.OrderServiceImpl")).getDeclaredMethods())[0];
        CGLIB$order$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "order", "CGLIB$order$0");
    }
    
    final void CGLIB$order$0() {
        super.order();
    }
    
    public final void order() {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Method, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$emptyArgs, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Proxy);
            return;
        }
        super.order();
    }
// 設(shè)置回調(diào)
    public void setCallbacks(final Callback[] array) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
    }
    
    static {
        CGLIB$STATICHOOK1();
    }
}
  • 方法索引
public int getIndex(final String s, final Class[] array) {
        Label_1079: {
            switch (s.hashCode()) {
                case 106006350: {
                    if (!s.equals("order")) {
                        break;
                    }
                    switch (array.length) {
                        case 0: {
                            return 7;
                        }
                        default: {
                            break Label_1079;
                        }
                    }
                    break;
                }
            }
        }
        return -1;
    }
    
    public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final OrderServiceImpl$$EnhancerByCGLIB$$279607fc orderServiceImpl$$EnhancerByCGLIB$$279607fc = (OrderServiceImpl$$EnhancerByCGLIB$$279607fc)o;
        try {
            switch (n) {
                case 7: {
                    orderServiceImpl$$EnhancerByCGLIB$$279607fc.order();
                    return null;
                }
            }
        }
        catch (Throwable t) {
            throw new InvocationTargetException(t);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

4).手寫CGLIB動態(tài)代理思路

/**
* 直接生成代理類,然后設(shè)置callback方法
*/
MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2 memberServiceImpl=
    new MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2();
// 設(shè)置回調(diào)
memberServiceImpl.setCallbacks(new CglibMethodInterceptor());
memberServiceImpl.addMember("jarye");

3.JDK和CGLIB動態(tài)代理

1).差異對比

對比點 JDK動態(tài)代理 CGLIB動態(tài)代理
代碼上 實現(xiàn)InvocationHandler接口,對目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理 實現(xiàn)MethodInterceptor接口的intercept方法
原理上 使用Java的反射技術(shù)生成繼承了Proxy類的動態(tài)匿名類,只能代理實現(xiàn)了接口的類, 沒有實現(xiàn)接口的類不能實現(xiàn)動態(tài)代理。 通過ASM字節(jié)碼處理框架來轉(zhuǎn)換字節(jié)碼并生成代理類的子類,子類重寫了被代理類中所有非final的方法,在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢植入橫切邏輯。大部分功能實際上是ASM所提供的,Cglib只是封裝了ASM,簡化了ASM操作,實現(xiàn)了運行期生成新的class。
缺陷 jdk動態(tài)代理,必須是面向接口,目標(biāo)業(yè)務(wù)類必須實現(xiàn)接口 對于被代理類中的final方法,無法進(jìn)行代理,因為子類中無法重寫final函數(shù)
總結(jié) jdk動態(tài)代理最終使用反射機(jī)制執(zhí)行目標(biāo)方法 cglib根據(jù)索引找到目標(biāo)方法,然后通過super.目標(biāo)方法執(zhí)行

2).Cglib的效率比Jdk動態(tài)代理效率要高原因【jdk7開始JDK動態(tài)代理更加高效】

  • Jdk動態(tài)動態(tài)代理 走回調(diào)攔截 實現(xiàn)接口接口生成帶了類 使用反射技術(shù)執(zhí)行我們的目標(biāo)方法
    a.拼接java源代碼
    b.編譯為class文件
    c.讀取去class文件到內(nèi)存中
  • Cglib動態(tài)代理 采用繼承的模式生成代理類 底層基于Asm字節(jié)碼技術(shù)實現(xiàn)生成代理類
    a.生成class文件
    b.讀取去class文件到內(nèi)存中
    c、采用fastClass索引的機(jī)制執(zhí)行我們的目標(biāo)方法

2).代理模式在Spring中的應(yīng)用

  • 如果目標(biāo)對象實現(xiàn)了接口,默認(rèn)情況下會采用JDK的動態(tài)代理實現(xiàn)AOP,但可以強(qiáng)制使用CGLIB實現(xiàn)
  • 如果目標(biāo)對象沒有實現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換
    Spring中的@Async注解使用不當(dāng)會導(dǎo)致失效,請參考:@Async失效之謎

相關(guān)文章鏈接:
<<<23種常用設(shè)計模式總覽
<<<裝飾模式(Decorator Pattern)
<<<觀察者模式(Observer Pattern)
<<<單例模式(Singleton Pattern)
<<<責(zé)任鏈模式(Chain of Responsibility Pattern)
<<<策略模式(Strategy Pattern)
<<<模板方法模式(Template Pattern)
<<<外觀/門面模式(Facade Pattern)
<<<建造者模式(Builder Pattern)
<<<適配器模式(Adapter Pattern)
<<<原型模式(Prototype Pattern)
<<<工廠相關(guān)模式(Factory Pattern)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、代理模式定義 給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常...
    AC編程閱讀 614評論 0 0
  • 代理模式(Proxy Pattern)是指由于某些原因需要給某個對象提供一個代理以控制對該對象的訪問,這時訪問對象...
    吉他手_c156閱讀 117評論 0 0
  • What: 為其他對象提供一種代理以控制對這個對象的訪問。 Why: 優(yōu)點: 1.增強(qiáng)目標(biāo)對象??梢栽趫?zhí)行目標(biāo)對象...
    愛打乒乓的程序員閱讀 2,890評論 0 1
  • 1.介紹 1.1定義 代理模式的定義:代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的...
    luoqiang108閱讀 268評論 0 0
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,833評論 16 22

友情鏈接更多精彩內(nèi)容