天天看點

Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析

代理模式

  • Java動态代理運用了設計模式中常用的代理模式
  • 代理模式:
    • 目的就是為其他對象提供一個代理用來控制對某個真實對象的通路
  • 代理類的作用:
    • 為委托類預處理消息
    • 過濾消息并轉發消息
    • 進行消息被委托類執行後的後續處理
    Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析

通過代理層這一中間層,有效的控制對于真實委托類對象的直接通路,同時又可以實作自定義的控制政策,比如Spring中的AOP機制,這樣使得在設計上獲得更大的靈活性

  • 代理的基本構成:
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 代理模式中有Subject角色 ,RealSubject角色和Proxy角色:
    • Subject: 負責定義RealSubject和Proxy角色應該實作的接口
    • RealSubject: 用來真正完成業務服務功能
    • Proxy: 負責将自身的Request請求,調用RealSubject對應的request功能實作業務功能,自身不做真正的業務
  • 靜态代理模式:
    • 當在代碼階段規定這種代理關系 ,Proxy類通過編譯器編譯成class檔案,當系統運作時,此class已經存在
    • 這種靜态代理模式可以通路無法通路的資源,增強現有的接口業務功能方面有很大的優點.但是大量的使用這種靜态代理,會使系統内的類規模增大,并且不易維護
    • 由于Proxy和RealSubject的功能本質上是相同的 ,Proxy隻是中介的作用,這種代理在系統中的存在,會造成代碼備援
  • 為了解決靜态代理模式的問題,就有了動态建立Proxy:
    • 在運作狀态中,需要代理的地方,根據Subject和RealSubject, 動态地建立一個Proxy
    • Proxy使用完之後,就會銷毀,這樣就可以避免Proxy角色的class在系統中的備援問題

Java動态代理

  • java.lang.reflect.Proxy:
    • Java動态代理機制的主類
    • 提供一組靜态方法為一組接口動态的生成對象和代理類
// 該方法用于擷取指定代理對象所關聯的調用處理器
public static InvocationHandler getInvocationHandler(Object proxy);

// 該方法用于擷取關聯于指定類裝載器和一組接口的動态代理類的類對象
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces); 

// 該方法用于判斷指定類對象是否是一個動态代理類
public static boolean isProxyClass(Class<?> cl);

// 該方法用于為指定類裝載器,一組接口以及調用處理器生成動态代理類執行個體
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);           
  • java.lang.reflect.InvocationHandler:
    • 調用處理器接口,自定義invoke方法用于實作對真正委托類的代理通路
/**
 * 該方法負責集中處理動态代理類上的所有方法調用
 * 
 * @param proxy 代理執行個體
 * @param method 被調用的方法對象
 * @param args 調用參數
 * @return 傳回調用處理器根據三個參數進行預處理或者分派到委托類執行個體上反射執行的對象
 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;           
  • java.lang.ClassLoader:
    • 類裝載器
    • 将類的位元組碼裝載到Java虛拟機即JVM中,并為其定義類對象,然後該類才能被使用
    • Proxy類與普通類的唯一差別就是 :Proxy類位元組碼是由JVM在運作時動态生成的而不是預存在于任何一個.calss檔案中
    • 每次生成動态代理類對象時都需要指定一個類裝載器對象

Java動态代理機制

Java動态代理建立對象的過程:

  • 通過實作InvocationHandler接口建立自己的調用處理器
/* 
 * InvocationHandlerImpl實作了InvocationHandler接口
 * 并能實作方法調用從代理類到委托類的分派轉發向委托類執行個體的引用,用于真正執行分派轉發過來的方法調用
 */
 InvocationHandler handler = new InvocationHandlerImpl(...);           
  • 通過為Proxy類指定ClassLoader對象和一組interface來建立動态代理類
// 通過Proxy為包括Interface接口在内的一組接口動态建立代理類的類對象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });           
  • 通過反射機制獲得動态代理類的構造函數,其唯一參數類型是調用處理器接口類型
// 通過反射從生成的類對象獲得構造函數對象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });           
  • 通過構造函數建立動态代理類執行個體,構造時調用處理器對象作為參數被傳入
// 通過構造函數建立動态代理類執行個體
Interface proxy = (Interface)constructor.newInstance(new Object[] { handler });           

為了簡化對象建立過程 ,Proxy類中使用newInstanceProxy封裝了步驟2-步驟4, 是以隻需要兩個步驟即可完成代理對象的建立

// InvocationHandlerImpl實作了InvocationHandler接口,并能實作方法調用從代理類到委托類的分派轉發
InvocationHandler handler = new InvocationHandlerImpl(...); 
// 通過 Proxy 直接建立動态代理類的執行個體
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler);           

Java動态代理注意點

  • 包:
    • 代理接口是public, 則代理類被定義在頂層包 ,package為空,否則default, 代理類被定義在該接口所在的包
/*
 * 記錄非公共代理接口的包,以便在同一個包中定義代理類
 * 驗證所有非公共代理接口是否都在同一個包中 
 */
for (int i =0; i < interfaces.length; i++ ) {
    int flags = interfaces[i].getModifiers();
    if (!Modifier.isPublic(flags)) {
        String name = interfaces[i].getName();
        int n = name.lastIndexOf(".");
        String pkg = ((n == -1) ? "" : name.subString(0, n+1));
        if (proxyPkg == null) {
            proxyPkg = pkg;
        } else if (!pkg.equals(proxyPkg)) {
            throw new IllegalArgumentException("non-public interfaces from different packaes");
        }
    }
}
        if (proxyPkg == null) {
        // 沒有使用非公共代理接口代理類的包
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }           
  • 生成的代理類為public final,不能被繼承
  • 類名的格式為 :"$ProxyN"
    • N是逐一遞增的數字,代表Proxy是被第N次動态代理生成的代理類
    • 對于同一組接口,接口的排列順序也相同,不會重複建立動态代理類,而是傳回一個先前已經建立并緩存了的代理類對象,以此提高效率
synchronized (cache) {
    /*
     * 不必擔心擷取到清除掉弱引用的緩存
     * 因為如果一個代理類已經被垃圾回收,代理類的類加載器也會被垃圾回收
     * 是以擷取到的緩存都是加載到緩存中的映射
     */
     do {
         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;
             }
         }while (true);
     }           
  • 類繼承關系:
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析

Proxy類是父類,這個規則适用于所有由Proxy建立的動态代理類(這也導緻Java動态代理的缺陷,由于Java不支援多繼承,是以無法實作對class的動态代理,隻能對于Interface進行代理),該類實作了所有代理的一組接口,是以Proxy類能夠被安全地類型轉換到其所代理的某接口

  • 代理類的根類java.lang.Object中的hashCode(),equals()和().toString方法同樣會被分派到調用處理器invoke方法執行

Java動态代理測試

建立一個動态代理類
public class serviceProxy implements InvocationHandler {
    private Object target;
    /**
     * 綁定委托對象并傳回一個代理對象
     * @param target 真實對象
     * @return 代理對象
     */
    public Object bind(Object target, Class[] interfaces) {
        this.target = target;
        // 取得代理對象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 通過代理對象調用方法首先進入這個方法
     * @param proxy 代理對象
     * @param Method 方法,被調用方法
     * @param args 方法的參數
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*
         * JDK動态代理
         */
         Object result = null;
         // 反射方法前調用
         System.err.println("--反射方法前執行的方法--");
         // 反射執行方法,相當于調用target.xXX()
         result = method.invoke(target, args);
         // 反射方法後調用
         System.err.println("--反射方法後執行的方法--");
         return result;
    }
}           
  • bind方法:
    • bind方法中的newProxyInstance方法,就是生成一個代理對象
      • 第一個參數: 類加載器
      • 第二個參數: 真實委托對象所實作的接口. 代理對象挂在那個接口下
      • 第三個參數: this代表目前HelloServiceProxy類, 也就是使用HelloServiceProxy作為對象的代理
  • invoke方法:
    • invoke方法有三個參數:
      • 第一個proxy是代理對象
      • 第二個是目前調用那個方法
      • 第三個是方法的參數
ProxyTest
public class ProxyTest {
    public static void main(String[] args) {
        HelloServiceProxy proxy = new HelloServiceProxy();
        HelloService service = new HelloServiceImpl();
        // 綁定代理對象
        service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
        service.sayHello("user");
    }
}           

class檔案分析

  • Java編譯器編譯好Java檔案後,産生 .class檔案在磁盤中:
    • class檔案是二進制檔案,内容是隻有JVM虛拟機能夠識别的機器碼
    • JVM虛拟機讀取位元組碼檔案,取出二進制資料
    • 加載到記憶體中,解析 .class檔案内的資訊,生成對應的Class對象
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 加載class檔案位元組碼到系統内,轉換成class對象,然後再執行個體化:
    • 定義一個類
    • 自定義一個類加載器,用于将位元組碼轉換成class對象
    • 編譯 .class檔案,在程式中讀取位元組碼,然後轉換成相應的class對象,再執行個體化

在運作期生成二進制位元組碼

  • 在代碼中,動态建立一個類:
    • 由于JVM通過位元組碼的二進制資訊加載類,是以在運作期的系統中,遵循Java編譯系統組織 .class檔案的格式和結構,生成相應的二進制資料,然後再把這個二進制資料加載轉換成對應的類
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 可以使用開源架構在運作時期按照Java虛拟機規範對class檔案的組織規則生成對應的二進制位元組碼. 比如ASM,Javassist

ASM

  • ASM是一個Java位元組碼操控架構:
    • 能夠以二進制形式修改已有類或者動态生成類
    • ASM在建立class位元組碼的過程中,操縱的級别是底層JVM彙編指令級别
    • ASM可以直接産生二進制class檔案,也可以在類被加載入Java虛拟機之前動态改變類行為
    • ASM從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者的要求生成新類
  • 通過ASM生成類的位元組碼:
    • 使用ASM架構提供的ClassWriter接口,通過通路者模式進行動态建立class位元組碼
    • 然後使用Java反編譯工具 (JD_GUI) 打開硬碟上生成的類.class檔案檢視類資訊
    • 再使用定義的類加載器将class檔案加載到記憶體中,然後建立class對象,并且執行個體化一個對象,調用code方法,檢視code方法中的結果
  • 至此表明: 在代碼裡生成位元組碼,并動态地加載成class對象,建立執行個體是完全可以實作的

Javassist

  • Javassist是一個開源的分析,編輯和建立Java位元組碼的類庫,已經加入JBoss應用伺服器項目,通過使用Javassist對位元組碼操作為JBoss實作動态AOP架構:
    • Javassist是JBoss一個子項目,主要優點在于簡單快速
    • 直接使用Java編碼的形式,不需要虛拟機指令,就能改變類的結構或者動态生成類

源碼分析

Proxy類
// 映射表: 用于維護類裝載器對象到其對應的代理類緩存
private static Map loaderToCache = new WeakHashMap();

// 标記: 用于标記一個動态代理類正在被建立中
private static Object pendingGenerationMarker = new Object();

// 同步表: 記錄已經被建立的動态代理類類型,主要通過方法isProxyClass進行相關判斷
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());

// 關聯的調用處理器引用
protected InvocationHandler h;           
newProxyInstance
  • Proxy靜态方法newProxyInstance:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    /*
     * 檢查關聯調用處理器是否為空,如果是空則抛出異常
     */
     if (h == null) {
         throw new NullPointerException();
     }
     /*
      * 獲得與指定類型裝載器和一組接口相關的代理類類型對象
      */
      Class<?> cl = getProxyClass0(loader, interfaces);
      /*
       * 通過反射擷取構造函數對象并生成代理類執行個體
       */
       try {
           final Constructor<?> cons = cl.getConstructor(constructorParams);
              final  InvocationHandler ih = h;
              SecurityManager sm = System.getSecurityManager();
              if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                  /* 
                   * 使用doPrivilege建立動态代理類執行個體
                   * 因為代理類實作可能需要特殊權限的非公共接口
                   */
                   return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                       public Object run() {
                           return newInstance(cons, ih);
                       }    
                   });
              } else {
                      return newInstance(cons, ih);
              }
       } catch (NoSuchMethodException e) {
               throw new InternalError(e.toString());
       }
}

private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h});
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantationException e) {
        throw new InternalException(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalException(e.toString());
        }
    }
}           
  • 動态代理的真正的關鍵是在getProxyClass0() 方法

getProxyClass0方法分析

  • 通過getProxyClass0方法中生成具體的class檔案的過程:
    • 定義path
    • 将class檔案寫到指定的硬碟中
    • 反編譯生成的class檔案

getProxyClass0() 方法分為四個步驟:

  1. 對這組接口進行一定程度的安全檢查:

1.1 接口類對象是否對類裝載器可見

1.2 接口類對象與類裝載器所識别的接口類對象完全相同

1.3 確定是interface類型而不是class類型.

for (int i = 0; i < interfaces.length; i++ ) {
    /*
     * 驗證類加載器是否将此接口的名稱解析為相同的類對象
     */
     String interfaceName = interface[i].getName();
     Class interfaceClass = null;
     try {
         /*
          * forName(String name, boolean initialize, ClassLoader loader)
          * Returns the Class object associated with the class or interface with the given string name,
          * using the given class loader
         */ 
         interfaceClass = Class.forName(interfaceName, false, loader);
     } catch (ClassNotFoundException e) {
     }
     if (interfaceClass != interface[i]) {
         throw new IllegalArgumentException(interface[i] + "is not visible from class loader.");
     }

    /*
     * 驗證類加載器得到的類對象是否是interface類型
     */
     if (! interfaceClass.isInterface()) {
         throw new IllegalArgumentException(interfaceClass.getName() + "is not an interface.");
     }

    /*
     * 驗證類加載器得到的類對象接口不是一個重複的接口
     */
     if (interfaceSet.contains(interfaceClass)) {
         throw new IllegalArgumentException("repeated interface:" + interface.getName());
     }
     interfaceSet.add(interfaceClass);
     interfaceName[i] = interfaceName;
}           
  1. 從loaderToCache映射表中擷取以類裝載器對象為關鍵字所對應的緩存表,如果不存在,就會建立一個新的緩存表并更新到loaderToCahe中:

2.1 loaderToCache存放鍵值對 : 接口名字清單:動态生成的代理類的類對象的引用

2.2 當代理類正在被建立時,會臨時進行儲存 : 接口名字清單:pendingGenerationMarker

2.3 标記pendingGenerationMarker的作用是通知後續的同類請求(接口數組相同且組内接口排列順序也相同)代理類正在被建立,請保持等待直至建立完成

/*
 * 尋找類加載器的緩存表,如果沒有就為類加載器建立代理類緩存
 */
Map cache;
synchronized (loaderToCache) {
    cache = (Map) loaderToCache.get(loader);
     if (cache == null) {
         cache = new HashMap();
         loaderToCache = put(loader, 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 (InterruptException e) {
         }
         // 等待被喚醒,繼續循環并通過二次檢查以確定建立完成,否則重新等待
         continue;
     } else {
         // 标記代理類正在被建立
         cache.put(key, pendingGenerationMarker);
         // 跳出循環已進入建立過程
         break;
     }
} while(true)           
  1. 動态建立代理類的class對象
/* 
 * 選擇一個名字代理類來生成
 */
long num;
synchronized (nextUniqueNumberLock) {
    num = nextUniqueNumber ++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
 * 驗證類加載器中沒有使用這個名字定義的類
 */
 ...
 
// 動态生成代理類的位元組碼數組
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);           

首先根據接口public與否的規則生成代理類的名稱 - $ProxyN格式,然後動态生成代理類. 所有的代碼生成工作由ProxyGenerator完成,該類在rt.jar中,需要進行反編譯

public static byte[] generateProxyClass(final String name, Class[] interfaces) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    // 動态生成代理類的位元組碼
    final byte[] classFile = gen.generateClassFile();
    // 如果saveGeneratedFiles的值為true,則會把所生成的代理類的位元組碼儲存到硬碟上
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    try{
                        FileOutputStream file = new FileOutputStream(doToSlash(name) + ".class");
                        file.write(classFile);
                        file.close();
                        return null;
                    } catch (IOException e) {
                        throw new InternalError("I/O exception saving generated file :" + e);
                    }
                }
            }
        );
    } 
    // 傳回代理類的位元組碼
    return classFile;
}           
  1. 代碼生成過程進入結尾部分,根據結果更新緩存表. 如果代理類成功生成則将代理類的類對象引用更新進緩存表,否則清除緩存表中對應的關鍵值,最後喚醒所有可能的正在等待的線程
finally {
    synchronized (cache) {
        if (proxyClass != null) {
            cache.put(key, new WeakReference(proxyClass));
        } else {
            cache.remove(key);
        }
        cache.notifyAll();
    }
}
return proxyClass;           

InvocationHandler解析

  • Proxy角色在執行代理業務的時候,就是在調用真正業務之前或者之後完成一些額外的功能
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 代理類就是在調用真實角色的方法之前或者之後做一些額外的業務
  • 為了構造具有通用性和簡單性的代理類,可以将所有的觸發真實角色動作交給一個觸發管理器,讓這個管理器統一管理觸發,這個觸發管理器就是InvocationHandler
  • 動态代理工作的基本工作模式:
    • 将方法功能的實作交給InvocationHandler角色
    • 外接對Proxy角色中每一個的調用 ,Proxy角色都會交給InvocationHandler來處理
    • InvocationHandler則調用具體對象角色的方法
Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 在這種模式中,代理Proxy和RealSubject應該實作相同的類的public方法,有兩種方式:
    • 一個比較直覺的方式: 就是定義一個功能接口,然後讓Proxy和RealSubject來實作這個接口 (JDK中的動态代理機制 - Java動态代理機制)
    • 比較隐晦的方式: 通過繼承實作Proxy繼承RealSubject. 因為Proxy繼承自RealSubject, 這樣Proxy則擁有了RealSubject的功能 ,Proxy還可以通過重寫RealSubject中的方法來實作多态(cglib)

JDK動态代理機制

  • JDK動态代理機制通過接口為RealSubject建立一個動态代理對象:
    • 擷取RealSubject上的所有接口清單
    • 确定要生成的代理類類名
    • 根據需要實作的接口資訊,在代碼中動态建立該Proxy類的位元組碼
    • 将對應的位元組碼轉換成對應的class對象
    • 建立InvocationHandler, 用來處理Proxy所有方法調用
    • Proxy的class對象,以建立的handler為參數,執行個體化一個proxy
  • JDK動态代理執行個體:
    • 定義兩個接口Vehicle和Rechargeable
    • Vehicle接口表示交通工具類,有drive()方法
    • Rechargeable接口表示可充電,有recharge()方法
    • 定義一個實作兩個接口的類ElectricCar,類圖如下:
    Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析
  • 建立ElectricCar的動态代理類:
/**
 * 交通工具接口
 */
 public interface Vehicle {
     public void drive();
 }           
/**
 * 充電接口
 */
 public interface Rechargable {
     public void recharge();
 }           
/**
 * 電動車類
 * 實作Rechargable, Vehicle接口
 */
 public class ElectricCar implements Rechargable, Vehicle {
     @Override
     public void drive() {
         System.out.println("ElectricCar can drive.");
     }
 
     @Override
     public void recharge() {
         System.out.println("ElectricCar can recharge.");
     }
 }           
/**
 * 動态代理類的觸發器
 */
 public class InvocationHandlerImpl implements InvocationHandler {
     private ElectricCar car;
     
     public InvocationHandlerImpl(Electric car) {
         this.car = car;
     } 
 
     @Override
     public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
         System.out.println("正在調用方法:" + paramMethod.getName() + "...");
         paramMethod.invoke(car, null);
         System.out.println("方法" + paramMethod.getName() + "調用結束.");
         return null;
     }
 }           
public class ProxyTest {
    public static void main(String[] args) {
        ElectricCar car = new ElectricCar();
        // 擷取對應的ClassLoader
        ClassLoader classLoader = car.getClass().getClassLoader();
        // 擷取ElectricCar所實作的所有接口
        Class[] interfaces = car.getClass().getInterfaces();
        // 設定一個來自代理傳過來的方法調用請求處理器,處理所有的代理對象上的方法調用
        InvocationHandler handler = new InvocationHandlerImpl(car);
        /*
           * 建立代理對象在這個過程中:
           *         a. JDK根據傳入的參數資訊動态地在記憶體中建立和 .class 等同的位元組碼
          *        b. 根據相應的位元組碼轉換成對應的class
          *        c. 然後調用newInstance()建立執行個體
          */
         Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
         Vehicle vehicle = (Vehicle) o;
         vehicle.drive();
         Rechargable rechargable = (Rechargable) o;
         rechargable.recharge();
    }
}           
  • 生成動态代理類的位元組碼并且儲存到硬碟中:
  • JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName, calss[] interfaces) 底層方法來産生動态代理類的位元組碼
  • 定義一個工具類,用來将生成的動态代理類儲存到硬碟中:
public class proxyUtils {
    /*
     * 根據類資訊,動态生成二進制位元組碼儲存到硬碟中
     * 預設是clazz目錄下
     * 
     * @params clazz 需要生成動态代理類的類 
     * @proxyName 動态生成代理類的名稱
     */
     public static void generateClassFile(Class clazz, String proxyName) {
         // 根據提供的代理類名稱和類資訊,生成位元組碼
         byte[] classFile = ProxyGenerator.generateProxyClass(ProxyName, clazz.getInterfaces());
         String paths = clazz.getResource(".").getPath();
         System.out.println(paths);
         FileOutputStream out = null;
         try {
             // 保留到硬碟中
             out = new FileOutputStream(paths + proxyName + ".class");
             out.write(classFile);
             out.flush();
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 out.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
}           
  • 修改代理類名稱為 "ElectricCarProxy", 并儲存到硬碟,使用以下語句:
ProxyUtils.generateClassFile(car.getClass(), "ElectricCarProxy");           
這樣将在ElectricCar.class同級目錄下産生ElectricCarProxy.class檔案
  • 使用反編譯工具jd-gui.exe打開,将會看到以下資訊:
/**
 * 生成的動态代理類的組織模式是繼承Proxy類,然後實作需要實作代理的類上的所有接口
 * 在實作過程中,是通過将所有的方法都交給InvocationHandler來處理
 */
 public final class ElectricCarProxy extends Proxy implements Rechargable,Vehicle {
     private static Method m1;
     private static Method m3;
     private static Method m4;
     private static Method m0;
     private static Method m2;

    public ElectricCarProxy(InvocationHandler paramInvocationHandler) throws {
        super(paramInvocationHandler);
    }  
 
     public final boolean equals(Object paramObject) throws {
         try {
             /*
              * 方法功能的實作交給InvocationHandler處理
             */
             return ((Boolean) this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue();
         } catch (Error | RuntimeException localError) {
             throw localError;
         } catch (Throwable localThrowable) {
             throw new Undeclared ThrowableException(localThrowable);
         }
     } 
     
     public final void recharge() throws {
         try {
             /*
              * 方法功能的實作交給InvocationHandler處理
              */
             this.h.invoke(this, m3, null);
             return;           
         } catch (Error | RuntimeException localError) {
             throw localError;
         } catch (Throwable localThrowable) {
             throw new Undeclared ThrowableException(localThrowable);
         }
     }
  
      public final drive() throws {
          try {
              /* 
                * 方法實作交給InvocationHandler處理
                */
               this.h.invoke(this, m4, null);
               return;
          } catch (Error | RuntimeException localError) {
              throw localError;
          } catch (Throwable localThrowable) {
              throw new Undeclared ThrowableException(localThrowable);
          }
      }
   
       public final int hasCode() throws {
           try {
               /*
                * 方法功能交給InvocationHandler處理
                */
               return ((Integer) this.h.invoke(this, m0, null)).intValue();
           } catch (Error | RuntimeException localError) {
               throw localError;
           } catch (Throwable localThrowable) {
               throw new Undeclared ThrowableException(localThrowable);
           }
       } 
   
       public final String toString() throws {
           try {
               /*
                * 方法功能實作交給InvocationHandler處理
                */
               return (String)this.h.invoke(this, m2, null);
           } catch (Error | RuntimeException localError) {
               throw localError;
           } catch (Throwable localThrowable) {
               throw new Undeclared ThrowableException(localThrowable);
           }
       }
   
    static {
           try {
               /*
                * 為每一個需要方法對象 
                * 當調用相應的方法時,分别将方法對象作為參數傳遞給InvocationHandler處理
                */
               m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
               m3 = Class.forName("com.oxford.proxy.Rechargable").getMethod("recharge", new Class[0]);
               m4 = Class.forName("com.oxford.proxy.Vehicle").getMethod("drive", new Class[0]);
               m0 = Class.forName("java.lang.Object").getMethod("hasCode", new Class[0]);
               m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
               return;
               } catch (NoSuchMethodException localNoSuchMethodException) {
                   throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
               } catch (ClassNotFoundException localClassNotFoundException) {
                   throw new NoClassDefFoundError(localClassNotFoundException.getMessge());
               }
       }
 }           
  • 生成的動态代理類的特點:
    • 繼承自java.lang.reflect.Proxy, 實作了Rechargable,Vehicle這兩個ElectricCar接口
    • 類中的所有方法都是final的
    • 所有的方法功能的實作都統一調用了InvocationHandler的invoke() 方法
    Java動态代理機制和實作原理詳解代理模式Java動态代理Java動态代理機制Java動态代理測試class檔案分析源碼分析getProxyClass0方法分析InvocationHandler解析

CGLIB動态代理機制

  • CGLIB通過類繼承生成動态代理類
  • JDK動态代理類的特點:
    • 某個類必須有實作的接口,而生成的代理類隻能代理某個類接口定以的方法. 這樣會導緻子類實作繼承兩個接口的方法外,另外實作的方法,在産生的動态代理類中不會有這個方法
    • 如果某個類沒有實作接口,那麼這個類就不能使用JDK動态代理了
  • CGLIB: Code Generation Library, CGLIB是一個強大的,高性能,高品質的Code生成類庫,可以在運作時期擴充Java類與實作Java接口
  • CGLIB建立類的動态代理類的模式:
    • 查找類中所有非final的public類型的方法定義
    • 将這些方法的定義轉換成位元組碼
    • 将組成的位元組碼轉換成相應的代理的class對象
    • 實作MethodInterceptor接口,用來處理對代理類上的所有方法的請求 (類似JDK動态代理中的InvocationHandler的作用)
  • 定義一個Programmer類,一個Hacker類
/**
 * 開發人員類
 */
 public class Programmer {
     public void code() {
         System.out.println("開發人員開發程式代碼.");
     }
 }           
/**
 * 黑客類
 * 實作了方法攔截器接口
 */
 public class Hacker implements MethodInterceptor {
     @Override
     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
         System.out.println("Hacker what to do.");
         proxy.invokeSuper(obj, args);
         System.out.println("Hacker done this.");
         return null;
     }
 }           
  • 測試類:
public class Test {
        public static void main(String[] args) {
            Programmer programmer = new Programmer();
            Hacker hacker = new Hacker();
            // CGLIB中的加強器,用來建立動态代理
            Enhancer enhancer = new Enhancer();
            // 設定要建立動态代理的類
            enhancer.setSuperclass(programmer.getClass());
            /*
             * 設定回調
             * 這裡相當于對于代理類上所有方法的調用,都會調用CallBack
             * 而CallBack則需要實行intercept()方法進行攔截
             */
            enhancer.setCallBack(hacker);
            Programmer proxy = (Programmer) enhancer.create();
            proxy.code();
        }
}           
  • 通過CGLIB生成的class檔案的内容:
public class Programmer EnhancerByCGLIB fa7aa2cd extends Programmer implements Factory {
    /* 
     * ....
     */
     
     // Enhancer傳入的methodInterceptor
     private MethodInterceptor CGLIB$CALLBACK_0;
     
     /*
      * ...
      */

      public final void code() {
          MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
          if (tmp4_1 == null) {
              tmp4_1;
              // 若callback不為空,則調用methodInterceptor的intercept()方法
              CGLIB$BIND_CALLBACKS(this);
          }
          if (this.CGLIB$CALLBACK_0 != null)
              return;
              // 如果沒有設定callback回調函數,則預設執行父類的方法
              super.code();
      }
      /*
       * ...
       */
}