1. 程式人生 > >Java設計模式-代理模式之動態代理(附原始碼分析)

Java設計模式-代理模式之動態代理(附原始碼分析)

具體有如下四步驟: 通過實現 InvocationHandler 介面建立自己的呼叫處理器;通過為 Proxy 類指定 ClassLoader 物件和一組 interface 來建立動態代理類;通過反射機制獲得動態代理類的建構函式,其唯一引數型別是呼叫處理器介面型別;通過建構函式建立動態代理類例項,構造時呼叫處理器物件作為引數被傳入。

一個具體的例子

接著上面的類圖和靜態代理中的例子,我們分別建立Subject和RealSubject Subject
package ProxyMode;

/*
 * 抽象介面,對應類圖中的Subject
 * 
 */

public interface Subject {

    public void SujectShow();

}


RealSubject
package ProxyMode;


public class RealSubject implements Subject{

    @Override
    public void SujectShow() {
        // TODO Auto-generated method stub
System.out.println("殺人是我指使的,我是幕後黑手!By---"+getClass()); } }
建立InvocationHandler用來響應代理的任何呼叫
package ProxyMode;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyHandler implements InvocationHandler {

    private Object proxied;   

      public
ProxyHandler( Object proxied ) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("準備工作之前:"); //轉調具體目標物件的方法 Object object= method.invoke( proxied, args); System.out.println("
工作已經做完了!"); return object; } }
動態代理類測試,這個代理類中再也不用實現Subject介面,可以動態的獲得RealSubject介面中的方法
package ProxyMode;


import java.lang.reflect.Proxy;

public class DynamicProxy  {

    public static void main( String args[] )   
      {   
        RealSubject real = new RealSubject();   
        Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), 
         new Class[]{Subject.class}, 
         new ProxyHandler(real));

        proxySubject.SujectShow();;

      }   
}

測試結果

準備工作之前:
殺人是我指使的,我是幕後黑手!By---class ProxyMode.RealSubject
工作已經做完了!

Proxy和InvocationHandler重要部分原始碼分析

java.lang.reflect.Proxy:這是 Java 動態代理機制的主類,它提供了一組靜態方法來為一組介面動態地生成代理類及其物件。

清單 1. Proxy 的靜態方法
// 方法 1: 該方法用於獲取指定代理物件所關聯的呼叫處理器,比如上面程式碼中的ProxyHandler
static InvocationHandler getInvocationHandler(Object proxy) 

// 方法 2:該方法用於獲取關聯於指定類裝載器和一組介面的動態代理類的類物件
static Class getProxyClass(ClassLoader loader, Class[] interfaces) 

// 方法 3:該方法用於判斷指定類物件是否是一個動態代理類
static boolean isProxyClass(Class cl) 

// 方法 4:該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
    InvocationHandler h)
下面重點看看newPRoxyInstance方法:

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

    // 檢查 h 不為 空,否則拋異常
    if (h == null) { 
        throw new NullPointerException(); 
    } 

    // 獲得與制定類裝載器和一組介面相關的代理類型別物件
    Class cl = getProxyClass(loader, interfaces); 

    // 通過反射獲取建構函式物件並生成代理類例項
    try { 
        Constructor cons = cl.getConstructor(constructorParams); 
        return (Object) cons.newInstance(new Object[] { h }); 
    } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
    } catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
    } catch (InstantiationException e) { throw new InternalError(e.toString()); 
    } catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
    } 
}
看這個方法的三個引數
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入interfaces: 一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了 h: 一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上 從上面JDK原始碼中可以看出getProxyClass方法才是newProxyInstance方法中最重要的,該方法負責為一組介面動態地生成代理類型別物件。下面開始解析proxy中的getProxyClass方法 該方法總共可以分為四個步驟: 對這組介面進行一定程度的安全檢查,包括檢查介面類物件是否對類裝載器可見並且與類裝載器所能識別的介面類物件是完全相同的,還會檢查確保是 interface 型別而不是 class 型別。

這個步驟通過一個迴圈來完成,檢查通過後將會得到一個包含所有介面名稱的字串陣列,記為 String[] interfaceNames

 for (int i = 0; i < interfaces.length; i++) {

            // 驗證類載入程 序 解 析 該介面到同一類物件的名稱。
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " is not visible from class loader");
            }

            // 驗證類物件真正代表一個介面

            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }

            //驗證這個介面是不是重複的
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass); //interfaceset是一個hashset集合

            interfaceNames[i] = interfaceName;
        }

從 loaderToCache 對映表中獲取以類裝載器物件為關鍵字所對應的快取表,如果不存在就建立一個新的快取表並更新到 loaderToCache。

快取表是一個 HashMap 例項,正常情況下它將存放鍵值對(介面名字列表,動態生成的代理類的類物件引用)。當代理類正在被建立時它會臨時儲存(介面名字列表,pendingGenerationMarker)。標記 pendingGenerationMarke 的作用是通知後續的同類請求(介面陣列相同且組內介面排列順序也相同)代理類正在被建立,請保持等待直至建立完成。

 synchronized (cache) {
do { 
    // 以介面名字列表作為關鍵字獲得對應 cache 值
    Object value = cache.get(key); 
    if (value instanceof Reference) { 
        proxyClass = (Class) ((Reference) value).get(); 
    } 
    if (proxyClass != null) { 
        // 如果已經建立,直接返回,這裡非常重要,如果已經建立過代理類,那麼不再建立
        return proxyClass; 
    } else if (value == pendingGenerationMarker) { 
        // 代理類正在被建立,保持等待
        try { 
            cache.wait(); 
        } catch (InterruptedException e) { 
        } 
        // 等待被喚醒,繼續迴圈並通過二次檢查以確保建立完成,否則重新等待
        continue; 
    } else { 
        // 標記代理類正在被建立
        cache.put(key, pendingGenerationMarker); 
        // break 跳出迴圈已進入建立過程
        break; 
} while (true);
}
動態建立代理類的類物件。

首先是確定代理類所在的包,其原則如前所述,如果都為 public 介面,則包名為空字串表示頂層包;如果所有非 public 介面都在同一個包,則包名與這些介面的包名相同;如果有多個非 public 介面且不同包,則拋異常終止代理類的生成。確定了包後,就開始生成代理類的類名,同樣如前所述按格式“$ProxyN”生成。

// 動態地生成代 理類的位元組碼陣列
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 
try { 
    // 動態地定義新生成的代理類
    proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, 
        proxyClassFile.length); 
} catch (ClassFormatError e) { 
    throw new IllegalArgumentException(e.toString()); 
} 

// 把生成的代理類的類物件記錄進 proxyClasses 表
proxyClasses.put(proxyClass, null);

到了這裡,其實generateProxyClass方法也是一個重點,但是generateProxyClass的方法程式碼跟蹤不了,位於並未公開的 sun.misc 包,有若干常量、變數和方法以完成這個神奇的程式碼生成的過程,但是 sun 並沒有提供原始碼以供研讀 結尾部分 根據結果更新快取表,如果成功則將代理類的類物件引用更新進快取表,否則清楚快取表中對應關鍵值,最後喚醒所有可能的正在等待的執行緒。

 synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }

java.lang.reflect.InvocationHandler:這是呼叫處理器介面,它自定義了一個 invoke 方法,用於集中處理在動態代理類物件上的方法呼叫,通常在該方法中實現對委託類的代理訪問。

InvocationHandler 的核心方法,我們最關心的是Invoke方法為什麼會被呼叫,見下面分析:
// 該方法負責集中處理動態代理類上的所 有方法呼叫。
//第一個引數既是代理類例項,
//第二個引數是被呼叫的方法物件
// 第三個方法是呼叫引數。呼叫處理器根據這三個引數進行預處理或分派到委託類例項上發射執行

Object invoke(Object proxy, Method method, Object[] args)

每次生成動態代理類物件時都需要指定一個實現了該介面的呼叫處理器物件(參見 newProxyInstance 的第三個引數)。 很多人肯定跟我一樣,我們在Handler中呼叫的method.invoke方法中並沒有顯示的呼叫invoke方法,只是在newProxyInstance中應用了一個handler物件,有了上面關於newProxyInstance的原始碼分析,我們知道了 newproxyinstance生成了一個$Proxy0類代理。當呼叫Subjectshow()方法時,其實呼叫的$Proxy0的SubjectShow()方法,從而呼叫父類Proxy中傳進來第三個引數(h)的的Invoke方法。
//這個方法是 Proxy原始碼中的
  protected Proxy(InvocationHandler h) {
        this.h = h;
    }

來看NewProxyInstance方法生成的$Proxy0代理類的原始碼

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });

            m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                    new Class[0]);

            m3 = Class.forName("***.RealSubject").getMethod("request",
                    new Class[0]);

            m2 = Class.forName("java.lang.Object").getMethod("toString",
                    new Class[0]);

        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    } //static

    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

    @Override
    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    @Override
    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void SubjectShow() {
        try {
            super.h.invoke(this, m3, null); //就是這個地方  呼叫h.invoke()
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    @Override
    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

從上面的$Proxy0中找到方法SubjectSHow()方法,我們可以看到中間呼叫了父類Proxy的引數Handler h的invoke方法,也就呼叫了ProxyHandler中的invoke()方法,還可以看到¥Proxy0還代理了equals()、hashcode()、tostring()這三個方法,至此動態代理實現機制就很清楚了

相關推薦

java設計模式動態代理的概述和實現

概述 1.代理:本來應該自己做的事情,請了別人來做,被請的人就是代理物件。 舉例:春節回家買票讓人代買              2.在Java中java.lang.reflect包

【八】Java設計模式GOF23動態代理(原生JDK和CGLIB)

一、使用JDK原生動態代理 基於Java反射機制。 Java動態代理是基於介面的,如果物件沒有實現介面則選擇用CGLIB方式實現動態代理。 實現步驟: 1.首先實現一個InvocationHandler,方法呼叫會被轉發到該類的invoke()方法。 2.然後在需要

Java設計模式-代理模式動態代理(原始碼分析)

具體有如下四步驟: 通過實現 InvocationHandler 介面建立自己的呼叫處理器;通過為 Proxy 類指定 ClassLoader 物件和一組 interface 來建立動態代理類;通過反射機制獲得動態代理類的建構函式,其唯一引數型別是呼叫處理器介面型別;通過建構函式建立動態代理類例項,構造時

Java設計模式Proxy動態代理

Java動態代理主要涉及到兩個類: InvocationHandler:該介面中僅定義了一個Object : invoke(Object proxy, Method method, Object[] args);引數proxy指代理類,method表示被代理的方法,args

JD設計模式——動態代理

每次 kkk getclass throwable ace his login target pro 動態代理的目的就是,用代理類 來幫助被代理類處理一些邏輯 1.首先我們寫一個被代理類(因為代理都是面向接口編程 先來寫一個接口) package javaee.net.c

設計模式動態代理

ner 我們 實現類 hand -o 技術 框架源碼 具體實現 voc 動態代理模式,在當前流行框架(如:Spring、Mybatis、Dubbo)中應用非常廣泛,掌握動態代理模式是理解框架源碼的基礎。如果未來你參與框架的設計與開發,動態代理模式必將成為你的利器。

設計模式動態代理模式

傳遞 api inter 服務 輸出 main 編程) 創建 實現 學習動態代理模式是為了以後學習AOP(面向切面編程)打下基礎,他比裝飾者模式還要靈活。 我們只學習他的一個方法: Proxy.newProxyInstance(ClassLoader classLoader

設計模式學習動態代理模式

import java.lang.reflect.Method; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; class InvocationHandlerImpl implements I

設計模式---動態代理

關於Java中的動態代理,我們首先需要了解的是一種常用的設計模式--代理模式,而對於代理,根據建立代理類的時間點,又可以分為靜態代理和動態代理。  一、代理模式 代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊

Java代理模式動態代理

    代理模式是設計模式中非常重要的一種型別。代理模式從型別上來說,可以分為靜態代理和動態代理兩種型別。     假設一個場景,有一個蛋糕店,賣的蛋糕都是用蛋糕機做的,而且不同種類的蛋糕由不同的蛋糕機來做,有水果蛋糕機,巧克力蛋糕機等。它們賣的麵包片也是麵包機做的,不同種

筆記14:設計模式動態代理

代理模式 * 概念: 1. 真實物件:被代理的物件 2. 代理物件: 3. 代理模式:代理物件代理真實物件,達到增強真實物件功能的目的 * 實現方式: 1. 靜態代理:有一個類檔案描述代理模式 2. 動態代理:在記憶體中形成代理類 * 實現步驟:

GOF23設計模式動態代理模式理解

 動態代理(dynamic Proxy) 動態代理(動態生成代理類) JDK自帶的動態代理 Javaassist位元組碼操作庫實現 CGLIB ASM(底層使用指令,可維護性較差

設計模式 動態代理模式

  Java動態代理類位於Java.lang.reflect包下,一般主要涉及到以下兩個類: (1). Interface InvocationHandler:該介面中僅定義了一個方法Object:invoke(Object obj,Method method, Obj

代理設計模式動態代理與靜態代理

在學習Spring框架的時候,有一個重要的思想就是AOP,面向切面程式設計,利用AOP的思想結合Spring的一些API可以實現核心業務與輔助業務的分離,即可以在執行核心業務時,將一些輔助的業務加進來,而輔助業務(如日誌,許可權控制等)一般是一些公共業務,這樣就實現了兩者的

【趣味設計模式系列】代理模式2--JDK動態代理原始碼解析】

## 1. 圖解 ![](https://img2020.cnblogs.com/blog/1765702/202008/1765702-20200813090502793-1476832292.png) 上圖主要描述了JDK動態代理的執行過程,下面做詳細分析。 ## 2. Proxy原始碼分析 上一篇

設計模式》之一文帶你理解單例、JDK動態代理、CGLIB動態代理、靜態代理

個人認為我在動態代理方面的分析算是比較深入了,下次更新再修改一下,爭取做到最好,後續還有建造者模式、模板方法、介面卡、外觀、責任鏈、策略和原型模式的深入!各位讀者如果覺得還不錯的可以持續關注哦。謝謝各位!!! 我的github,到時上傳例子程式碼 https://github.com

java代理模式學習,靜態代理,JDK動態代理,CGLIB動態代理

                java代理模式學習,靜態代理,JDK動態代理,CGLIB動態代理   一、理解代理 1、代理,Proxy 。意思是:本來該你做的事兒,別人代替你去做。 比如說:即將

談談java代理模式的認識二——動態代理(JDK)

 讓我們就接著上篇部落格的靜態代理來開始今天的動態代理。 一、動態代理              靜態代理需要在執行之前就寫好代理類,這樣就造成了程式碼的大量重複,所以我們通過動態代理在執行時期動態生

dubbo原始碼動態代理模式生成proxy過程

跟蹤消費方建立代理物件的過程在ReferenceConfig中實現 //在init方法中建立代理物件,init在工廠方法FactoryBean的getObject()中呼叫 ref = createProxy(map); /** * 在createP

dubbo原始碼動態代理模式生成Invoker過程

// 原始碼中proxyFactory生成Invoker根據spi機制預設使用JavaassistRpcProxyFactory Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class)