動態(tài)代理(JDK動態(tài)代理剖析)

動態(tài)代理

代理模式

概念:為其它對象提供一種代理以控制對這個對象的訪問
本質:觸發(fā)被代理安排;但是執(zhí)行者還是被代理本身執(zhí)行(包括動態(tài)代理)

類圖

代理模式UML.png
  • Subject抽象主題角色

    可以是抽象類也可以是接口

  • RealSubject具體主題角色

    委托角色、被代理角色(業(yè)務邏輯的具體執(zhí)行者)

  • Proxy代理主題角色

    委托類、代理類;它負責對真實角色的應用,把所有抽象主題類定義的方法委托給真實主題角色實現(xiàn),并且在真實主題角色處理完畢前后做預處理和善后處理的工作。

    抽象主題類

    public interface Subject {
        void method();
    }
    

    真實主題類

    public class RealSubject implements Subject {
        //實現(xiàn)方法
        @Override
        public void method() {
            //具體業(yè)務處理
        }
    }
    

    代理類

    public class Proxy implements Subject {
        //要代理的對象
        private Subject subject;
        //構造函數(shù)中;傳入要代理的對象
        public Proxy(Subject realSubject) {
            this.subject = realSubject;
        }
        //實現(xiàn)接口中定義的方法
        @Override
        public void method() {
            //預處理
            this.before();
            //代理的對象執(zhí)行業(yè)務邏輯
            this.subject.method();
            //善后處理
            this.after();
        }
        private void before() {
            //預處理
        }
        private void after() {
            //善后處理
        }
    }
    

為什么使用代理

  • 職責清晰

    真實的角色就是實現(xiàn)實際的業(yè)務邏輯,不用關心其它非本職的工作

  • 高擴展

  • 補充功能

動態(tài)代理

概念

在實現(xiàn)階段不關心代理誰,而在運行階段才指定代理哪個對象

為什么會產(chǎn)生動態(tài)代理

  • 靜態(tài)代理代理類和被代理類實現(xiàn)了相同的接口,導致代碼重復,可擴展性差(被代理類增加方法,代理類也要實現(xiàn)這個方法)
  • 靜態(tài)代理代理類只服務于一種類型的代理

動態(tài)代理分類

動態(tài)代理主要分為JDK動態(tài)代理和cglib動態(tài)代理兩大類

JDK動態(tài)代理

必要條件:必須有被代理類的接口

UML圖

動態(tài)代理UML.png

抽象被代理角色

public interface Subject {
    //業(yè)務操作
    public void doSomething(String str);
}

真實被代理角色

public class RealSubject implements Subject{
    //業(yè)務操作
    @Override
    public void doSomething(String str) {
        System.out.println("do something------> " + str);
    }
}

動態(tài)代理Handler

public class MyInvocationHandler implements InvocationHandler{
    //被代理的對象
    private Object target;
    //通過構造函數(shù)傳入被代理對象
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    //參數(shù)1:proxy 動態(tài)代理對象
    //參數(shù)2:method 正在執(zhí)行的方法
    //參數(shù)3:args 調用目標方法時傳入的實參
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        //執(zhí)行被代理的方法
        return method.invoke(this.target, args);
    }

測試類1

public class Client1 {
    public static void main(String[] args) {
        //定義被代理類
        Subject realSubject = new RealSubject();
        //定義一個handler
        InvocationHandler handler = new MyInvocationHandler(realSubject);
        //動態(tài)代理類
        //第一個參數(shù):用于定義代理類的類加載器;傳入被代理類接口的類加載器(動態(tài))
         //注意 第一個參數(shù)加載器 不是handler的加載器,handler不是代理類
        //第二個參數(shù):被代理類的接口
        //第三個參數(shù):InvocationHandler,用來處理方法的調用
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(), 
                realSubject.getClass().getInterfaces(), 
                handler);
        //代理的行為
        proxy.doSomething("Finish");
    }

}

輸出:

do something------> Finish

測試類2

public class Client2 {
    public static void main(String[] args) 
            throws Exception {
        //定義被代理類
        Subject realSubject = new RealSubject();
        //定義一個handler
        InvocationHandler handler = new MyInvocationHandler(realSubject);
        //使用Proxy生成一個動態(tài)代理類ProxyClass
        Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), 
                new Class[] {Subject.class});
        //獲取proxyClass類中一個invocationHandler參數(shù)的構造器
        Constructor<?> constructor = 
                proxyClass.getConstructor(new Class[] {InvocationHandler.class});
        //調用constructor 的newInstance方法創(chuàng)建動態(tài)實例
        Subject proxy = (Subject) constructor.newInstance(new Object[] {handler});
        //代理的行為
        proxy.doSomething("Finish");
    }
}

輸出:

do something------> Finish

JDK動態(tài)代理分析

Proxy

提供用于創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它也是所有動態(tài)代理類的父類。

Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例

public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)    

創(chuàng)建一個動態(tài)代理類所對應的class對象,該代理類將實現(xiàn)interfaces所指定的所有接口。如以上測試類2

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

直接創(chuàng)建一個動態(tài)代理對象,該代理對象實現(xiàn)了interfaces指定的系列接口,執(zhí)行代理對象的每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法

動態(tài)代理類由輸出到硬盤

public class CreateProxyClass {
    public static void main(String[] args) {
        // 使用Proxy生成一個動態(tài)代理類ProxyClass
        Class<?> proxyClass = Proxy.getProxyClass(
                Subject.class.getClassLoader(), 
                new Class[] { Subject.class });
        //將動態(tài)生成的代理類生成字節(jié)碼;并輸出到D盤Proxy.class
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy", 
                proxyClass.getInterfaces());
        String path = "D://Proxy.class";
        try (FileOutputStream out = new FileOutputStream(path);){
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

查看 D://Proxy.class

import com.dwb.design.proxy.demo1.*;
import java.lang.reflect.*;

public final class $Proxy extends Proxy implements Subject
{
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    
    public $Proxy(final InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    
    public final boolean equals(final Object o) {
        try {
            return (boolean)super.h.invoke(this, $Proxy.m1, new Object[] { o });
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    public final String toString() {
        try {
            return (String)super.h.invoke(this, $Proxy.m2, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    //重點查看該方法:我們寫的業(yè)務方法;
    public final void doSomething(final String s) {
        try {
            //對應到 InvocationHandler的三個參數(shù);為什么第一個為動態(tài)代理類
            //解釋了,調用方法最后都會調用重寫的InvocationHandler 的invoke方法
            super.h.invoke(this, $Proxy.m3, new Object[] { s });
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    public final int hashCode() {
        try {
            return (int)super.h.invoke(this, $Proxy.m0, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    static {
        try {
            $Proxy.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            $Proxy.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
            $Proxy.m3 = Class.forName("com.dwb.design.proxy.demo1.Subject").getMethod("doSomething", Class.forName("java.lang.String"));
            $Proxy.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
}

運行時動態(tài)代理UML

運行時動態(tài)代理UML.png

說明:藍色部分為運行時生成的代理對象$Proxy; 該代理類繼承了Proxy、實現(xiàn)了被代理接口、組合了InvocationHandler;

代理對象執(zhí)行過程:

  1. 先調用自身實現(xiàn)的被代理接口方法
  2. 調用組合的invocation對象,invoke方法(參數(shù)1:代理對象,參數(shù)2:被代理接口方法,參數(shù)3:方法參數(shù))
  3. 反射執(zhí)行被代理方法;invocation對象中invoke方法體內(nèi)method.invoke(this.target, args)

未完(CGLIB)

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

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

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