java動態代理Proxy源碼解析(Jdk 1.6)

一、 源碼

package java.lang.reflect;

import java.lang.ref.Reference;

import java.lang.ref.WeakReference;

import java.util.Arrays;

import java.util.Collections;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

import java.util.WeakHashMap;

import sun.misc.ProxyGenerator;

 

public class Proxy implements java.io.Serializable {

 

private static final long serialVersionUID = -2222568056686623797L;

 

/** prefix for all proxy class names  所有的代理類名稱的前綴*/

private final static String proxyClassNamePrefix = "$Proxy";

 

/** parameter types of a proxy class constructor代理類的構造函數的參數類型*/

private final static Class[] constructorParams = { InvocationHandler.class };

 

/** maps a class loader to the proxy class cache for that loader爲一個映射類加載器到代理類的加載器進行緩存*/

private static Map loaderToCache = new WeakHashMap();

 

/** marks that a particular proxy class is currently being generated */

private static Object pendingGenerationMarker = new Object();

 

/** next number to use for generation of unique proxy class names */

private static long nextUniqueNumber = 0;

private static Object nextUniqueNumberLock = new Object();

 

/** set of all generated proxy classes, for isProxyClass implementation */

private static Map proxyClasses = Collections

.synchronizedMap(new WeakHashMap());

 

/**

* the invocation handler for this proxy instance.

* 代理實例的調用處理器

* 此代理實例的調用處理程序。

* @serial

*/

protected InvocationHandler h;

 

/**

* Prohibits instantiation. 禁止實例化

*/

private Proxy() {

}

 

/**

* Constructs a new <code>Proxy</code> instance from a subclass (typically,

* a dynamic proxy class) with the specified value for its invocation

* handler.

*/

protected Proxy(InvocationHandler h) {

this.h = h;

}

 

 

public static Class<?> getProxyClass(ClassLoader loader,

Class<?>... interfaces) throws IllegalArgumentException {

if (interfaces.length > 65535) {

throw new IllegalArgumentException("interface limit exceeded");

}

 

Class proxyClass = null;

 

/* collect interface names to use as key for proxy class cache 

收集接口的名稱用於代理類緩存中的鍵*/

String[] interfaceNames = new String[interfaces.length];

 

Set interfaceSet = new HashSet(); // for detecting duplicates

 

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

/*

* Verify that the class loader resolves the name of this interface

* to the same Class object.

* 通過類加載器去加載,驗證類實現是否實現接口

*/

String interfaceName = interfaces[i].getName();

Class interfaceClass = null;

try { //參數false表示類加載器加載時候不解釋improt  

interfaceClass = Class.forName(interfaceName, false, loader);

} catch (ClassNotFoundException e) {

}

if (interfaceClass != interfaces[i]) {

throw new IllegalArgumentException(interfaces[i]

+ " is not visible from class loader");

}

 

/*

* Verify that the Class object actually represents an interface.

*/

if (!interfaceClass.isInterface()) {   // 驗證是不是接口

throw new IllegalArgumentException(interfaceClass.getName()

+ " is not an interface");

}

 

/*

* Verify that this interface is not a duplicate.

* 驗證接口是否重複

*/

if (interfaceSet.contains(interfaceClass)) {

throw new IllegalArgumentException("repeated interface: "

+ interfaceClass.getName());

}

interfaceSet.add(interfaceClass);

 

interfaceNames[i] = interfaceName;

}

 

/*

* Using string representations of the proxy interfaces as keys in the

* proxy class cache (instead of their Class objects) is sufficient

* because we require the proxy interfaces to be resolvable by name

* through the supplied class loader, and it has the advantage that

* using a string representation of a class makes for an implicit weak

* reference to the class.

用代理接口的字符串形式表示key存在緩存里爾不是Class對象足夠了,因爲我們需要代理接口去通過類加載器用名字渲染。並且這樣帶來的好處是使用字符串形式表述class能不明顯的依賴class

*/

Object key = Arrays.asList(interfaceNames);

 

/*

* Find or create the proxy class cache for the class loader.

*/

Map cache;

synchronized (loaderToCache) {

cache = (Map) loaderToCache.get(loader);

if (cache == null) {

cache = new HashMap();

loaderToCache.put(loader, cache);

}

/*

* This mapping will remain valid for the duration of this method,

* without further synchronization, because the mapping will only be

* removed if the class loader becomes unreachable.

*/

}

 

/*

* Look up the list of interfaces in the proxy class cache using the

* key. This lookup will result in one of three possible kinds of

* values: null, if there is currently no proxy class for the list of

* interfaces in the class loader, the pendingGenerationMarker object,

* if a proxy class for the list of interfaces is currently being

* generated, or a weak reference to a Class object, if a proxy class

* for the list of interfaces has already been generated.

*/

synchronized (cache) {

/*

* Note that we need not worry about reaping the cache for entries

* with cleared weak references because if a proxy class has been

* garbage collected, its class loader will have been garbage

* collected as well, so the entire cache will be reaped from the

* loaderToCache map.

* 因爲一個代理class會被垃圾回收, 

* 它的類加載器也會被垃圾回收,所以實體緩存會從loaderToCache map中獲得 

* 三種情況: 

* 1如果當前緩存總沒有此Proxy類則返回null 

* 2如果Proxy類正在創建則返回pendingGenerationMarker對象 

* 3如果此Proxy類已經被創建,則返回WeakReference對象

*/

do {

Object value = cache.get(key);

if (value instanceof Reference) {

proxyClass = (Class) ((Reference) value).get();

}

if (proxyClass != null) {

// proxy class already generated: return it

return proxyClass;

} else if (value == pendingGenerationMarker) {

// proxy class being generated: wait for it

try {

cache.wait();

} catch (InterruptedException e) {

/*

* The class generation that we are waiting for should

* take a small, bounded time, so we can safely ignore

* thread interrupts here.

*/

}

continue;

} else {

/*

* No proxy class for this list of interfaces has been

* generated or is being generated, so we will go and

* generate it now. Mark it as pending generation.

*/

cache.put(key, pendingGenerationMarker);

break;

}

} while (true);

}

 

try {

String proxyPkg = null; // package to define proxy class in

 

/*

* Record the package of a non-public proxy interface so that the

* proxy class will be defined in the same package. Verify that all

* non-public proxy interfaces are in the same package.

*/

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 packages");

}

}

}

 

if (proxyPkg == null) { // if no non-public proxy interfaces,

proxyPkg = ""; // use the unnamed package

}

 

{

/*

* Choose a name for the proxy class to generate.

*/

long num;

//這裏使用靜態屬性作爲鎖,保護了同步區域內的數據  

//因爲num是long型的,無法對原始對象加鎖(只能對類加鎖)  

//而如果對整個當前對象加鎖會浪費性能  

//使用一個鎖對象,還可達到同時保護多個數據(對象,原始類型)的效果

synchronized (nextUniqueNumberLock) {

num = nextUniqueNumber++;

}

String proxyName = proxyPkg + proxyClassNamePrefix + num;

/*

* Verify that the class loader hasn't already defined a class

* with the chosen name.

*/

 

/*

* Generate the specified proxy class.

*/

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

proxyName, interfaces);

try {

//加載代理類

proxyClass = defineClass0(loader, proxyName,

proxyClassFile, 0, proxyClassFile.length);

} catch (ClassFormatError e) {

/*

* A ClassFormatError here means that (barring bugs in the

* proxy class generation code) there was some other invalid

* aspect of the arguments supplied to the proxy class

* creation (such as virtual machine limitations exceeded).

*/

throw new IllegalArgumentException(e.toString());

}

}

// add to set of all generated proxy classes, for isProxyClass

proxyClasses.put(proxyClass, null);

 

} finally {

/*

* We must clean up the "pending generation" state of the proxy

* class cache entry somehow. If a proxy class was successfully

* generated, store it in the cache (with a weak reference);

* otherwise, remove the reserved entry. In all cases, notify all

* waiters on reserved entries in this cache.

*/

synchronized (cache) {

if (proxyClass != null) {

   //key是一堆接口的集合,併除去標記正在生成代理的pending generation對象  

cache.put(key, new WeakReference(proxyClass));

} else {

cache.remove(key);

}

cache.notifyAll();

}

}

return proxyClass;

}

 

//類加載器,接口的Class數組,類加載器,通常是APPClassLoader  

public static Object newProxyInstance(ClassLoader loader,

Class<?>[] interfaces, InvocationHandler h)

throws IllegalArgumentException {

if (h == null) {

throw new NullPointerException();

}

 

/*

* Look up or generate the designated proxy class.

//生成代理類的Class對象  

*/

Class cl = getProxyClass(loader, interfaces);

 

/*

* Invoke its constructor with the designated invocation handler.

*/

try {

//獲得有一個InvocationHandler對象參數的構造函數

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 boolean isProxyClass(Class<?> cl) {

if (cl == null) {

throw new NullPointerException();

}

 

return proxyClasses.containsKey(cl);

}

 

 

public static InvocationHandler getInvocationHandler(Object proxy)

throws IllegalArgumentException {

/*

* Verify that the object is actually a proxy instance.

*/

if (!isProxyClass(proxy.getClass())) {

throw new IllegalArgumentException("not a proxy instance");

}

 

Proxy p = (Proxy) proxy;

return p.h;

}

 

private static native Class defineClass0(ClassLoader loader, String name,

byte[] b, int off, int len);

}

二、 API文檔說明

java.lang.reflect 

類 Proxy

java.lang.Object

   java.lang.reflect.Proxy

所有已實現的接口: 

Serializable 

________________________________________

public class Proxy

extends Object

implements Serializable

Proxy 提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。 

創建某一接口 Foo 的代理: 

     InvocationHandler handler = new MyInvocationHandler(...);

     Class proxyClass = Proxy.getProxyClass(

         Foo.class.getClassLoader(), new Class[] { Foo.class });

     Foo f = (Foo) proxyClass.

         getConstructor(new Class[] { InvocationHandler.class }).

         newInstance(new Object[] { handler });

 

或使用以下更簡單的方法: 

     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

                                          new Class[] { Foo.class },

                                          handler);

 

動態代理類(以下簡稱爲代理類)是一個實現在創建類時在運行時指定的接口列表的類,該類具有下面描述的行爲。 代理接口 是代理類實現的一個接口。 代理實例 是代理類的一個實例。 每個代理實例都有一個關聯的調用處理程序 對象,它可以實現接口 InvocationHandler。通過其中一個代理接口的代理實例上的方法調用將被指派到實例的調用處理程序的 Invoke 方法,並傳遞代理實例、識別調用方法的 java.lang.reflect.Method 對象以及包含參數的 Object 類型的數組。調用處理程序以適當的方式處理編碼的方法調用,並且它返回的結果將作爲代理實例上方法調用的結果返回。 

代理類具用以下屬性: 

代理類是公共的、最終的,而不是抽象的。 

未指定代理類的非限定名稱。但是,以字符串 "$Proxy" 開頭的類名空間應該爲代理類保留。 

代理類擴展 java.lang.reflect.Proxy。 

代理類會按同一順序準確地實現其創建時指定的接口。 

如果代理類實現了非公共接口,那麼它將在與該接口相同的包中定義。否則,代理類的包也是未指定的。注意,包密封將不阻止代理類在運行時在特定包中的成功定義,也不會阻止相同類加載器和帶有特定簽名的包所定義的類。 

由於代理類將實現所有在其創建時指定的接口,所以對其 Class 對象調用 getInterfaces 將返回一個包含相同接口列表的數組(按其創建時指定的順序),對其 Class 對象調用 getMethods 將返回一個包括這些接口中所有方法的 Method 對象的數組,並且調用 getMethod 將會在代理接口中找到期望的一些方法。 

如果 Proxy.isProxyClass 方法傳遞代理類(由 Proxy.getProxyClass 返回的類,或由 Proxy.newProxyInstance 返回的對象的類),則該方法返回 true,否則返回 false。 

代理類的 java.security.ProtectionDomain 與由引導類加載器(如 java.lang.Object)加載的系統類相同,原因是代理類的代碼由受信任的系統代碼生成。此保護域通常被授予 java.security.AllPermission。 

每個代理類都有一個可以帶一個參數(接口 InvocationHandler 的實現)的公共構造方法,用於設置代理實例的調用處理程序。並非必須使用反射 API 才能訪問公共構造方法,通過調用 Proxy.newInstance 方法(將調用 Proxy.getProxyClass 的操作和調用帶有調用處理程序的構造方法結合在一起)也可以創建代理實例。 

代理實例具有以下屬性: 

提供代理實例 proxy 和一個由其代理類 Foo 實現的接口,以下表達式將返回 true: 

    proxy instanceof Foo

 

並且以下的強制轉換操作將會成功(而不拋出 ClassCastException): 

     (Foo) proxy

 

每個代理實例都有一個關聯的調用處理程序,它會被傳遞到其構造方法中。靜態 Proxy.getInvocationHandler 方法將返回與作爲其參數傳遞的代理實例相關的調用處理程序。 

代理實例上的接口方法調用將按照該方法的文檔描述進行編碼,並被指派到調用處理程序的 Invoke 方法。 

在代理實例上的 java.lang.Object 中聲明的 hashCode、equals 或 toString 方法的調用將按照與編碼和指派接口方法調用相同的方式進行編碼,並被指派到調用處理程序的 invoke 方法,如上所述。傳遞到 invoke 的 Method 對象的聲明類是 java.lang.Object。代理類不重寫從 java.lang.Object 繼承的代理實例的其他公共方法,所以這些方法的調用行爲與其對 java.lang.Object 實例的操作一樣。 

在多代理接口中重複的方法

當代理類的兩個或多個接口包含一個具有相同名稱和參數簽名的方法時,代理類的接口順序變得非常重要。在代理實例上調用重複方法 時,傳遞到調用處理程序的 Method 對象沒有必要成爲其聲明類可以從接口(通過該接口調用代理方法)的引用類型指派的對象。此限制存在的原因是,生成的代理類中的相應方法實現無法確定它通過哪一個接口調用。因此,在代理實例上調用重複方法時,第一個接口中的方法的 Method 對象包含接口的代理類列表中的方法(直接或通過超級接口繼承),該對象會傳遞到調用處理程序的 invoke 方法,無論該方法調用通過哪一種引用類型發生。 

如果代理接口包含某一方法,它的名稱和參數簽名與 java.lang.Object 的 hashCode、equals 或 toString 方法相同,那麼在代理實例上調用這樣的方法時,傳遞到調用處理程序的 Method 對象將使 java.lang.Object 成爲其聲明類。換句話說,java.lang.Object 公共的非最終方法理論上在所有代理接口之前,以便確定哪一個 Method 對象傳遞到調用處理程序。 

還要注意,當重複方法被指派到調用處理程序時,invoke 方法只可以拋出經過檢查的異常類型,該異常類型可以使用所有 代理接口(可以通過它調用)中方法的 throws 子句指派一種異常類型。如果 invoke 方法拋出一個經過檢查的異常,該異常沒有指派給任何由一個代理接口(可以通過它調用)中的方法聲明的異常類型,那麼該代理實例上的調用將拋出一個未經檢查的 UndeclaredThrowableException。此限制表示並非所有的由傳遞到 invoke 方法的 Method 對象上調用 getExceptionTypes 返回的異常類型都可以由 invoke 方法成功拋出。 

從以下版本開始: 

1.3 

另請參見: 

InvocationHandler, 序列化表格

________________________________________

字段摘要

protected  InvocationHandler

          此代理實例的調用處理程序。

 

構造方法摘要

protected Proxy(InvocationHandler h) 

          使用其調用處理程序的指定值從子類(通常爲動態代理類)構建新的 Proxy 實例。

 

方法摘要

static InvocationHandler

getInvocationHandler(Object proxy) 

          返回指定代理實例的調用處理程序。

static Class<?>

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

          返回代理類的 java.lang.Class 對象,並向其提供類加載器和接口數組。

static boolean isProxyClass(Class<?> cl) 

          當且僅當指定的類通過 getProxyClass 方法或 newProxyInstance 方法動態生成爲代理類時,返回 true。

static Object

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

          返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。

 

從類 java.lang.Object 繼承的方法

 

clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

 

 

字段詳細信息

h

protected InvocationHandler h

此代理實例的調用處理程序。 

構造方法詳細信息

Proxy

protected Proxy(InvocationHandler h)

使用其調用處理程序的指定值從子類(通常爲動態代理類)構建新的 Proxy 實例。 

參數: 

h - 此代理實例的調用處理程序

方法詳細信息

getProxyClass

public static Class<?> getProxyClass(ClassLoader loader,

                                     Class<?>... interfaces)

                              throws IllegalArgumentException

返回代理類的 java.lang.Class 對象,並向其提供類加載器和接口數組。該代理類將由指定的類加載器定義,並將實現提供的所有接口。如果類加載器已經定義了具有相同排列接口的代理類,那麼現有的代理類將被返回;否則,類加載器將動態生成並定義這些接口的代理類。 

對可以傳遞給 Proxy.getProxyClass 的參數有以下幾個限制: 

interfaces 數組中的所有 Class 對象必須表示接口,而不能表示類或基本類型。 

interfaces 數組中的兩個元素不能引用同一 Class 對象。 

所有接口類型的名稱通過特定的類加載器必須可見。換句話說,對於類加載器 cl 和所有接口 i,以下表達式必須爲 true: 

    Class.forName(i.getName(), false, cl) == i

 

所有非公共接口必須位於同一包中;否則,該代理類將不可能實現所有的接口,無論它在哪一個包中定義。 

對於有相同簽名的指定接口中任何成員方法集: 

o 如果任何方法的返回類型是基本類型或 void,那麼所有的方法必須具有與此相同的返回類型。 

o 否則,該方法之一必須是返回類型,它可以指派給該方法其餘的所有返回類型。 

得到的代理類必須不超過虛擬機在類上施加的任何限制。例如,虛擬機可以限制某一類實現至多 65535 的接口數;在這種情況下,interfaces 數組的大小必須不超過 65535。 

如果違反了這些限制,Proxy.getProxyClass 將拋出 IllegalArgumentException。如果 interfaces 數組參數或其任何元素爲 null,則將拋出 NullPointerException。 

注意,指定的代理接口的順序非常重要:對接口組合相同但順序不同的代理類的兩個請求會導致兩個不同的代理類。 

參數: 

loader - 定義代理類的類加載器 

interfaces - 代理類要實現的接口列表 

返回: 

用指定的類加載器定義的代理類,它可以實現指定的接口 

拋出: 

IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數上的任何限制 

NullPointerException - 如果 interfaces 數組參數或其任何元素爲 null

________________________________________

newProxyInstance

public static Object newProxyInstance(ClassLoader loader,

                                      Class<?>[] interfaces,

                                      InvocationHandler h)

                               throws IllegalArgumentException

返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。此方法相當於: 

     Proxy.getProxyClass(loader, interfaces).

         getConstructor(new Class[] { InvocationHandler.class }).

         newInstance(new Object[] { handler });

 

Proxy.newProxyInstance 拋出 IllegalArgumentException,原因與 Proxy.getProxyClass 相同。 

參數: 

loader - 定義代理類的類加載器 

interfaces - 代理類要實現的接口列表 

h - 指派方法調用的調用處理程序 

返回: 

一個帶有代理類的指定調用處理程序的代理實例,它由指定的類加載器定義,並實現指定的接口 

拋出: 

IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數上的任何限制 

NullPointerException - 如果 interfaces 數組參數或其任何元素爲 null,或如果調用處理程序 h 爲 null

________________________________________

isProxyClass

public static boolean isProxyClass(Class<?> cl)

當且僅當指定的類通過 getProxyClass 方法或 newProxyInstance 方法動態生成爲代理類時,返回 true。 

此方法的可靠性對於使用它做出安全決策而言非常重要,所以此方法的實現不應僅測試相關的類是否可以擴展 Proxy。 

參數: 

cl - 要測試的類 

返回: 

如該類爲代理類,則爲 true,否則爲 false 

拋出: 

NullPointerException - 如果 cl 爲 null

________________________________________

getInvocationHandler

public static InvocationHandler getInvocationHandler(Object proxy)

                                              throws IllegalArgumentException

返回指定代理實例的調用處理程序。 

參數: 

proxy - 返回調用處理程序的代理實例 

返回: 

代理實例的調用處理程序 

拋出: 

IllegalArgumentException - 如果參數不是一個代理實例

 

三、 解析

1.驗證InvocationHandler是否空

2.生成代理類的Class對象,這裏戲份比較多,代碼中詳細註釋。

(1).通過類加載器去加載得到Class實例,比較是否想通的Class

(2).判斷是否是接口

(3).去重Class,並通過String[]保存接口名

(4).從緩存中創建根據類加載器取得Map,如果沒有則創建一個,保存到WeakHashMap

(5). put key,value<一堆接口,正在創建代理的標誌> into Map

(6).記錄包可見的代理接口,for循環中是校驗非公有接口是否在同一包下

(7).生成類名,並生成代理類

(8).把代理類放入代理類緩存

(9).獲得有一個InvocationHandler對象參數的構造函數,並創建實例.

 

 

Java代碼   

1. public static Object newProxyInstance(ClassLoader loader,  

2.       Class<?>[] interfaces,  

3.       InvocationHandler h)//這裏有三個參數,第一個是傳入classloader,一般情況是傳入當前的classloader.但是我在上一節模擬實現裏傳入的是URL loader..第二個參數表示的是接口,第三個是Invocationhandler,除了第一個參數,其他和我在上一節裏的一樣.JDK的封裝的比較好,所以他傳入的是Interface的數組,  

4. throws IllegalArgumentException  

5.    {  

6. if (h == null) {  

7.     throw new NullPointerException();//如果Invocationhandler 爲空,拋異常  

8. }  

9.  

10. /* 

11.  * Look up or generate the designated proxy class.這個方法裏最主要的地方在這裏,它直接通過調用 getProxyClass方法獲取到了自動生成的動態代理類的二進制碼.在我上一節的內容裏,我們是通過自己生成JAVA文件,然後通過JAVA文件動態編譯成對應的class文件,然後通過URLClassLoader.loadClass("com.cjb.proxy.Proxy1");這個方法來獲取對應的二進制碼的.這個方法下面會繼續解釋. 

12.  */  

13. Class cl = getProxyClass(loader, interfaces);  

14.  

15. /* 

16.  * Invoke its constructor with the designated invocation handler.這個方法就是獲取對應class的Constructor,然後通過這個Constructor來實例化.. 

17.  */  

18. try {  

19.     Constructor cons = cl.getConstructor(constructorParams);  

20.     return (Object) cons.newInstance(new Object[] { h });  

21. } catch (NoSuchMethodException e) {  

22.     throw new InternalError(e.toString());  

23. } catch (IllegalAccessException e) {  

24.     throw new InternalError(e.toString());  

25. } catch (InstantiationException e) {  

26.     throw new InternalError(e.toString());  

27. } catch (InvocationTargetException e) {  

28.     throw new InternalError(e.toString());  

29. }  

30.    }  

31.  

32. 上面的方法,除了最重要的getProxyClass,其他都很容易理解.,那麼下面開始讀getProxyClass方法  

33.  

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

35.                                         Class<?>... interfaces)  

36. throws IllegalArgumentException  

37.    {  

38. if (interfaces.length > 65535) {  

39.     throw new IllegalArgumentException("interface limit exceeded");//JDK想的果然比較到位,連interface傳的太多都想到了..~!~  

40. }  

41.  

42. Class proxyClass = null;//這個就是最後要生成的二進制碼,首先初始化一下  

43.  

44. /* collect interface names to use as key for proxy class cache */  

45. String[] interfaceNames = new String[interfaces.length];//這個存放的是對應的Interface的名字..  

46.  

47. Set interfaceSet = new HashSet(); //這個HashSet 是爲了檢測interface重複記錄的.  

48.  

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

50.     /* 

51.      * Verify that the class loader resolves the name of this 

52.      * interface to the same Class object. 

53.      */  

54.     String interfaceName = interfaces[i].getName();  

55.     Class interfaceClass = null;  

56.     try {  

57.  interfaceClass = Class.forName(interfaceName, false, loader);//創建對應接口的二進制碼,第二個參數false表示,不需要初始化  

58.     } catch (ClassNotFoundException e) {  

59.     }  

60.     if (interfaceClass != interfaces[i]) {//如果創建出來的二進制碼和原來的接口不一樣,表示這個接口對於這個classloader來說是不可見的  

61.  throw new IllegalArgumentException(  

62.      interfaces[i] + " is not visible from class loader");  

63.     }  

64.  

65.     /* 

66.      * Verify that the Class object actually represents an 

67.      * interface. 

68.      */  

69.     if (!interfaceClass.isInterface()) {//如果不是接口,那麼就拋異常,這裏就規定了,我們必須通過接口來代理..或者說,必須面向接口編程  

70.  throw new IllegalArgumentException(  

71.      interfaceClass.getName() + " is not an interface");  

72.     }  

73.  

74.     /* 

75.      * Verify that this interface is not a duplicate.前面說,InterfaceSet是拿來判斷對應的interface接口是否有重複的.這裏的方法是:在循環interfaces的時候,把每個interface都添加到interfaceSet裏..當然,在添加之前會判斷,當前循環到的接口在InterfaceSet裏是否有,如果已經有了,則拋異常,說這個接口重複了..沒有,則添加.. 

76.      */  

77.     if (interfaceSet.contains(interfaceClass)) {  

78.  throw new IllegalArgumentException(  

79.      "repeated interface: " + interfaceClass.getName());  

80.     }  

81.     interfaceSet.add(interfaceClass);  

82.  

83.     interfaceNames[i] = interfaceName;//這句就是把每個接口名放到interfaceNames數組裏..  

84. }  

85.  

86. /* 

87.  * Using string representations of the proxy interfaces as 

88.  * keys in the proxy class cache (instead of their Class 

89.  * objects) is sufficient because we require the proxy 

90.  * interfaces to be resolvable by name through the supplied 

91.  * class loader, and it has the advantage that using a string 

92.  * representation of a class makes for an implicit weak 

93.  * reference to the class. 

94.  */  

95. Object key = Arrays.asList(interfaceNames);//這裏把Interface數組轉換成list..這裏直接寫成了Object類型  

96.  

97. /* 

98.  * Find or create the proxy class cache for the class loader. 

99.  */  

100. Map cache;//放緩存的Map  

101. synchronized (loaderToCache) {//loaderToCache也是一個Map.它的key是classloader,對應的value是對應的緩存,也是一個HashMap.他把對應的不同的classloader放到loaderToCache裏,如果下次還要調用這個方法創建代理,並傳入的是同一個classloader,那麼可以直接從cache裏取..增加速度.當然,如果沒有,則創建一條記錄,放到loaderToCache裏  

102.     cache = (Map) loaderToCache.get(loader);  

103.     if (cache == null) {  

104.  cache = new HashMap();  

105.  loaderToCache.put(loader, cache);  

106.     }  

107.     /* 

108.      * This mapping will remain valid for the duration of this 

109.      * method, without further synchronization, because the mapping 

110.      * will only be removed if the class loader becomes unreachable. 

111.      */  

112. }  

113.  

114. /* 

115.  * Look up the list of interfaces in the proxy class cache using 

116.  * the key.  This lookup will result in one of three possible 

117.  * kinds of values: 

118.  *     null, if there is currently no proxy class for the list of 

119.  *         interfaces in the class loader, 

120.  *     the pendingGenerationMarker object, if a proxy class for the 

121.  *         list of interfaces is currently being generated, 

122.  *     or a weak reference to a Class object, if a proxy class for 

123.  *         the list of interfaces has already been generated. 

124.  */  

125. synchronized (cache) {  

126.     /* 

127.      * Note that we need not worry about reaping the cache for 

128.      * entries with cleared weak references because if a proxy class 

129.      * has been garbage collected, its class loader will have been 

130.      * garbage collected as well, so the entire cache will be reaped 

131.      * from the loaderToCache map. 

132.      */  

133.     do {  

134.  Object value = cache.get(key);//這裏從cache裏獲取對應的Object..第一次執行的話,明顯獲取到的是Null..key表示的是用接口名字轉換而來的list..這個可以看上面的代碼.  

135.  if (value instanceof Reference) {  

136.      proxyClass = (Class) ((Reference) value).get();//如果已經能獲取到了,那麼,我們需要的二進制碼文件就是這個獲取到的  

137.  }  

138.  if (proxyClass != null) {  

139.      // proxy class already generated: return it  

140.      return proxyClass;  

141.  } else if (value == pendingGenerationMarker) {//這裏的pendingGenerationMarker是一個靜態常量,表示 new Object().JDK給出的 解釋是,如果代理正在創建,那麼等待他  

142.      // proxy class being generated: wait for it  

143.      try {  

144.   cache.wait();  

145.      } catch (InterruptedException e) {  

146.   /* 

147.    * The class generation that we are waiting for should 

148.    * take a small, bounded time, so we can safely ignore 

149.    * thread interrupts here. 

150.    */  

151.      }  

152.      continue;  

153.  } else {  

154.      /* 

155.       * No proxy class for this list of interfaces has been 

156.       * generated or is being generated, so we will go and 

157.       * generate it now.  Mark it as pending generation. 

158.       */  

159.      cache.put(key, pendingGenerationMarker);//如果cache裏獲取到的對應於key的value是Null ,那麼,就創建一個object對象放進去.上面說了,pendingGenerationMarker= new Object();  

160.      break;  

161.  }  

162.     } while (true);  

163. }  

164.  

165. try {  

166.     String proxyPkg = null; // package to define proxy class in 這個是代理類的包名  

167.  

168.     /* 

169.      * Record the package of a non-public proxy interface so that the 

170.      * proxy class will be defined in the same package.  Verify that 

171.      * all non-public proxy interfaces are in the same package. 

172.      */  

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

174.  int flags = interfaces[i].getModifiers();//getModifiers()方法返回的是該接口的修飾類型,用Int類型表示  

175.  if (!Modifier.isPublic(flags)) {//如果不是public 的接口..  

176.  

177.  //我們可以這樣理解,我們動態創建的代理,他的修飾類型必須是和接口的修飾類型是一樣的,我們知道,接口可以是public或者默認,兩種修飾類型.這裏的判斷如果不是public接口,那麼,該接口肯定是默認的,如果是默認修飾類型,那麼,它只能被同一個包下面的類看到,所以,就必須爲該代理類創建一個包名..當然,如果是public的話,就沒必要,因爲,反正所有的類都能看到..  

178.      String name = interfaces[i].getName();  

179.      int n = name.lastIndexOf('.');  

180.      String pkg = ((n == -1) ? "" : name.substring(0, n + 1));//注意這裏的N+1 ,其實是包括 "."的..比如 com.cjb.proxy.Proxy..它返回的就是"com.cjb.proxy."注意最後的那個點  

181.  

182.      if (proxyPkg == null) {  

183.   proxyPkg = pkg;  

184.      } else if (!pkg.equals(proxyPkg)) {  

185.   throw new IllegalArgumentException(  

186.       "non-public interfaces from different packages");  

187.      }  

188.  }  

189.     }  

190.  

191.     if (proxyPkg == null) { // if no non-public proxy interfaces,這裏可以看到,如果是public 的接口,對應代理類的包名就是"",也就是沒有包名  

192.  proxyPkg = "";  // use the unnamed package  

193.     }  

194.  

195.     {  

196.  /* 

197.   * Choose a name for the proxy class to generate. 

198.   */  

199.  long num;  

200.  synchronized (nextUniqueNumberLock) {  

201.      num = nextUniqueNumber++;//Num是一個計數器.用處是,創建代理的類名的時候用..我們可以看到,它是初始值是0.然後,每被調用一次,Num++.  

202.  }  

203.  String proxyName = proxyPkg + proxyClassNamePrefix + num;//proxyPkg是之前生成的包名, proxyClassNamePrefix 是一個靜態常量,proxyClassNamePrefix="$Proxy".最後Num是計數器.也就是說,它的代理類的名字是從 $Proxy1 $Proxy2 $Proxy3一直在增長的,這樣的話,就避免了重複.  

204.  /* 

205.   * Verify that the class loader hasn't already 

206.   * defined a class with the chosen name. 

207.   */  

208.  

209.  /* 

210.   * Generate the specified proxy class.下面打紅字的兩個方法是最後生成代理對象的..但是,很悲劇的是,他是用native修飾的,也就是說,它是不是用java來實現的..也就是說,最最關鍵的地方,不是用java實現的... 

211.   */  

212.  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  

213.      proxyName, interfaces);  

214.  try {  

215.      proxyClass = defineClass0(loader, proxyName,  

216.   proxyClassFile, 0, proxyClassFile.length);  

217.  } catch (ClassFormatError e) {  

218.      /* 

219.       * A ClassFormatError here means that (barring bugs in the 

220.       * proxy class generation code) there was some other 

221.       * invalid aspect of the arguments supplied to the proxy 

222.       * class creation (such as virtual machine limitations 

223.       * exceeded). 

224.       */  

225.      throw new IllegalArgumentException(e.toString());  

226.  }  

227.     }  

228.     // add to set of all generated proxy classes, for isProxyClass  

229.     proxyClasses.put(proxyClass, null);  

230.  

231. } finally {  

232.     /* 

233.      * We must clean up the "pending generation" state of the proxy 

234.      * class cache entry somehow.  If a proxy class was successfully 

235.      * generated, store it in the cache (with a weak reference); 

236.      * otherwise, remove the reserved entry.  In all cases, notify 

237.      * all waiters on reserved entries in this cache. 

238.      */  

239.     synchronized (cache) {  

240.  if (proxyClass != null) {  

241.      cache.put(key, new WeakReference(proxyClass));  

242.  } else {  

243.      cache.remove(key);  

244.  }  

245.  cache.notifyAll();  

246.     }  

247. }  

248. return proxyClass;  

249.    }  

 

 

Java的動態代理機制早在其1.3版本就已經引入了。在jdk1.5中,動態代理機制主要由兩個類來實現,他們是Proxy類和InvocationHandler接口,他們都可以在java.lang.reflect包下面找到。 

InvocationHandler接口裏面只有一個方法invoke,爲了使用的java的動態代理,用戶需要實現這個接口,並且在invoke方法中寫好調用實際方法的代碼,同時還能在調用實際方法的前後加上其他的邏輯,比如日誌或者事物操作等等。這個接口的實現類最終會被由jdk生成的動態代理類使用來調用實際的方法。關於如何使用java動態代理,請參閱網上其它文章,下面將要就Proxy生成動態代理類的源碼進行分析。 

在Proxy類裏面,有兩個方法跟動態代理類的生成有關,他們是: 

Java代碼   

1. public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,  

2. InvocationHandler h) throws IllegalArgumentException;  

3.  

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

5. throws IllegalArgumentException  

 

第一個方法比較簡單,代碼如下: 

Java代碼   

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

2. throws IllegalArgumentException{  

3.   if (h == null) {  

4.      throw new NullPointerException();  

5.   }  

6.   /* 

7.    * Look up or generate the designated proxy class. 

8.    */  

9.   Class cl = getProxyClass(loader, interfaces);  

10.   /* 

11.    * Invoke its constructor with the designated invocation handler. 

12.    */  

13.   try {  

14.      Constructor cons = cl.getConstructor(constructorParams);  

15.      return (Object) cons.newInstance(new Object[] { h });  

16.   } catch (NoSuchMethodException e) {  

17.      throw new InternalError(e.toString());  

18.   } catch (IllegalAccessException e) {  

19.      throw new InternalError(e.toString());  

20.   } catch (InstantiationException e) {  

21.      throw new InternalError(e.toString());  

22.   } catch (InvocationTargetException e) {  

23.      throw new InternalError(e.toString());  

24.   }  

25. }  

 

可以看到,在newProxyInstance方法內部調用了getProxyClass方法,然後使用反射機制來創建由getProxyClass返回的動態代理類的實例。所以讓我們重點來看一下getProxyClass這個方法。首先要說明一下在getProxyClass裏面用到的幾個類私有變量的作用。 

Java代碼   

1. /** maps a class loader to the proxy class cache for that loader */  

2. private static Map loaderToCache = new WeakHashMap();  

3. 這個變量用來保存某個類加載器以及使用該加載器加載的動態代理類集合。key爲類加載器,  

4. value則是一個HashMap,裏面放了具體的動態代理類,和用來索引的key。使用WeakHashMap是處於性能的考慮  

5. /** marks that a particular proxy class is currently being generated */  

6. private static Object pendingGenerationMarker = new Object();  

7. 用來標識一個動態代理類正在被創建,主要用來處理多線程的情況  

8. /** next number to use for generation of unique proxy class names */  

9. private static long nextUniqueNumber = 0;//用來生成唯一的動態代理類名時候用到  

10. private static Object nextUniqueNumberLock = new Object();//鎖對象  

11. /** set of all generated proxy classes, for isProxyClass implementation */  

12. private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());  

13. 這個變量用來保存所有生成的所有動態代理類,主要是爲了方便另外一個方法isProxyClass的使用設置的,在getProxyClass這個方法裏面是沒有什麼用處的。同樣適用的WeakHashMap,也是出於性能的考慮,不至於因爲proxyClasses持有動態代理類的應用而導致垃圾回收器沒法回收不再使用的動態代理類。  

 

好了,介紹了變量,我們可以來關注一下getProxyClass方法了,這個方法大致可以分爲三步,第一步是把傳入的interface類的名字保存下來,第二步檢查要用戶請求的動態代理類是否已經存在了,如果已經存在那麼就直接返回之前創建好的動態代理類。第三步就是當用戶請求的動態代理類不存在的時候去創建這個動態代理類。讓我們分別看一下這幾部分的代碼。 

[list]

•  第一步:

Java代碼   

1.      /* collect interface names to use as key for proxy class cache */  

2. String[] interfaceNames = new String[interfaces.length];  

3.  

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

5.    /* 

6.     * Verify that the class loader resolves the name of this 

7.     * interface to the same Class object. 

8.     */  

9.    String interfaceName = interfaces[i].getName();  

10.    Class interfaceClass = null;  

11.    try {  

12.    interfaceClass = Class.forName(interfaceName, false, loader);  

13.    } catch (ClassNotFoundException e) {  

14.    }  

15.    if (interfaceClass != interfaces[i]) {  

16.    throw new IllegalArgumentException(  

17.        interfaces[i] + " is not visible from class loader");  

18.    }  

19.  

20.    /* 

21.     * Verify that the Class object actually represents an 

22.     * interface. 

23.     */  

24.    if (!interfaceClass.isInterface()) {  

25.    throw new IllegalArgumentException(  

26.        interfaceClass.getName() + " is not an interface");  

27.    }  

28.  

29.    interfaceNames[i] = interfaceName;  

30. }  

 

這一步比較好理解,就是獲得各個interface類的名字,然後保存到一個字符串數組中。其中做了好幾步的檢查。需要說明的是,這個生成數組將會被用來作爲索引動態代理類的key。

•  第二步:

Java代碼   

1.       //將之前生成的接口名字數組轉成List,用來作爲基於這組接口生成的動態代理  

2.       //類的索引  

3.       Object key = Arrays.asList(interfaceNames);  

4.       /* 

5. * 檢查對於作爲參數輸入的類加載器是否已經創建過了動態代理類緩存,如果沒有 

6.        * 就創建一個,如果有就取出來。 

7. */  

8. Map cache;  

9. synchronized (loaderToCache) {  

10.    cache = (Map) loaderToCache.get(loader);  

11.    if (cache == null) {  

12.    cache = new HashMap();  

13.    loaderToCache.put(loader, cache);  

14.    }  

15.          //下面這段話說明了使用WeakHashMap的好處  

16.    /* 

17.     * This mapping will remain valid for the duration of this 

18.     * method, without further synchronization, because the mapping 

19.     * will only be removed if the class loader becomes unreachable. 

20.     */  

21. }  

22.       //下面這段代碼用來檢查實現了指定接口的動態代理是否已經被創建過了。  

23.       synchronized (cache) {  

24.           do{  

25.               //嘗試用key從動態代理cache中取動態代理  

26.    Object value = cache.get(key);  

27.    if (value instanceof Reference) {  

28.       proxyClass = (Class) ((Reference) value).get();  

29.    }  

30.               //如果動態代理已經存在,那麼就直接返回找到的動態代理  

31.    if (proxyClass != null) {  

32.    // proxy class already generated: return it  

33.       return proxyClass;  

34.    }  

35.               //如果沒有找到動態代理,但是發現key對應的是一個  

36.               //pendingGenerationMarker對象,那就說面之前已經有別的線程已經在  

37.               //創建這個動態代理了,所以進入等待狀態。負責創建的動態代理的  

38.               //那個線程創建完動態代理之後就會notify所有在cache上wait的線程  

39.               //這些線程被激活後就會繼續執行do循環,然後發現動態代理已經被創建  

40.               //了,所以就直接返回被創建了動態代理  

41.               else if (value == pendingGenerationMarker) {  

42.    // proxy class being generated: wait for it  

43.       try {  

44.        cache.wait();  

45.       } catch (InterruptedException e) {  

46.      /* 

47.           * The class generation that we are waiting for should 

48.       * take a small, bounded time, so we can safely ignore 

49.       * thread interrupts here. 

50.       */  

51.       }  

52.        continue;  

53.       }  

54.                  //如果發現自己是第一個要創建動態代理的線程,就在對應的key上  

55.                  //放置pendingGenerationMarker標誌對象  

56.                  else {  

57.       /* 

58.        * No proxy class for this list of interfaces has been 

59.        * generated or is being generated, so we will go and 

60.        * generate it now.  Mark it as pending generation. 

61.                   */  

62.        cache.put(key, pendingGenerationMarker);  

63.         break;  

64.         }  

65.    } while (true);  

66. }  

•  第三步:創建動態代理

Java代碼   

1.      try {  

2.          //在動態代理的接口不是public的情況下,保存動態代理所在的包  

3.   String proxyPkg = null;  // package to define proxy class in  

4.  

5.   /* 

6.    * Record the package of a non-public proxy interface so that the 

7.    * proxy class will be defined in the same package.  Verify that 

8.    * all non-public proxy interfaces are in the same package. 

9.    */  

10.          //在動態代理的接口不是public的情況下,找出動態代理應該被創建在哪個包中  

11.          //如果出現兩個不同的包的非public Inteface就拋錯  

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

13. int flags = interfaces[i].getModifiers();  

14. if (!Modifier.isPublic(flags)) {  

15.    String name = interfaces[i].getName();  

16.    int n = name.lastIndexOf('.');  

17.    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  

18.    if (proxyPkg == null) {  

19.    proxyPkg = pkg;  

20.    }   

21.                  else if (!pkg.equals(proxyPkg))   

22.                  {  

23.      throw new IllegalArgumentException(  

24.        "non-public interfaces from different packages");  

25.     }  

26. }  

27.   }  

28.   if (proxyPkg == null) {  // if no non-public proxy interfaces,  

29. proxyPkg = "";      // use the unnamed package  

30.   }  

31.  

32.   {  

33. /* 

34. * Choose a name for the proxy class to generate. 

35. */  

36.              //去一個數字,用來作爲生成的動態代理的一部分  

37. long num;  

38. synchronized (nextUniqueNumberLock) {  

39.    num = nextUniqueNumber++;  

40. }  

41.              //動態代理類的名字,proxyClassNamePrefix是固定的,值爲"$Proxy"  

42.              //java中合法的類名和變量名可以以$符開頭  

43. String proxyName = proxyPkg + proxyClassNamePrefix + num;  

44. /* 

45. * Verify that the class loader hasn't already 

46. * defined a class with the chosen name. 

47. */  

48.  

49. /* 

50. * Generate the specified proxy class. 

51. */  

52.              //下面就是創建動態代理的類文件,然後把生成的動態代理類加載進  

53.              //JVM中,下面用到的幾個方法是java語言比較低層的機制,這裏就  

54.              //不說了,有興趣的可以看看源代碼  

55. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  

56.    proxyName, interfaces);  

57. try {  

58.    proxyClass = defineClass0(loader, proxyName,  

59.    proxyClassFile, 0, proxyClassFile.length);  

60. } catch (ClassFormatError e) {  

61.    /* 

62.     * A ClassFormatError here means that (barring bugs in the 

63.     * proxy class generation code) there was some other 

64.     * invalid aspect of the arguments supplied to the proxy 

65.     * class creation (such as virtual machine limitations 

66.     * exceeded). 

67.     */  

68.    throw new IllegalArgumentException(e.toString());  

69. }  

70.   }  

71.   // add to set of all generated proxy classes, for isProxyClass  

72.   proxyClasses.put(proxyClass, null);  

73.  

74. finally {  

75.   /* 

76.    * We must clean up the "pending generation" state of the proxy 

77.    * class cache entry somehow.  If a proxy class was successfully 

78.    * generated, store it in the cache (with a weak reference); 

79.    * otherwise, remove the reserved entry.  In all cases, notify 

80.    * all waiters on reserved entries in this cache. 

81.    */  

82.          //這裏是用來擦屁股的,首先把生成的動態代理類加到對應的cache裏面,然後  

83.          //去激活所有被同一個cache阻塞的線程,通知他們動態代理已經生成的,好讓   

84.          //他們繼續執行。  

85.          //如果動態代理因爲各種情況沒有生成成功,那麼就把cache裏面的key刪除掉  

86.   synchronized (cache) {  

87. if (proxyClass != null) {  

88.    cache.put(key, new WeakReference(proxyClass));  

89. } else {  

90.    cache.remove(key);  

91. }  

92. cache.notifyAll();  

93.   }  

94.  

95.      return proxyClass;  

 

 

 

[java] view plaincopy

1. 讀源碼,怕過兩天又忘記了,還是記錄下吧..  

2.  

3. 動態代理最重要的實現就是Proxy.newInstance ,那我們直接看這個方法  

4.  

5. public static Object newProxyInstance(ClassLoader loader,  

6.       Class<?>[] interfaces,  

7.       InvocationHandler h)//這裏有三個參數,第一個是傳入classloader,一般情況是傳入當前的classloader.但是我在上一節模擬實現裏傳入的是URL loader..第二個參數表示的是接口,第三個是Invocationhandler,除了第一個參數,其他和我在上一節裏的一樣.JDK的封裝的比較好,所以他傳入的是Interface的數組,  

8. throws IllegalArgumentException  

9.    {  

10. if (h == null) {  

11.     throw new NullPointerException();//如果Invocationhandler 爲空,拋異常  

12. }  

13.  

14. /* 

15.  * Look up or generate the designated proxy class.這個方法裏最主要的地方在這裏,它直接通過調用 getProxyClass方法獲取到了自動生成的動態代理類的二進制碼.在我上一節的內容裏,我們是通過自己生成JAVA文件,然後通過JAVA文件動態編譯成對應的class文件,然後通過URLClassLoader.loadClass("com.cjb.proxy.Proxy1");這個方法來獲取對應的二進制碼的.這個方法下面會繼續解釋. 

16.  */  

17. Class cl = getProxyClass(loader, interfaces);  

18.  

19. /* 

20.  * Invoke its constructor with the designated invocation handler.這個方法就是獲取對應class的Constructor,然後通過這個Constructor來實例化.. 

21.  */  

22. try {  

23.     Constructor cons = cl.getConstructor(constructorParams);  

24.     return (Object) cons.newInstance(new Object[] { h });  

25. } catch (NoSuchMethodException e) {  

26.     throw new InternalError(e.toString());  

27. } catch (IllegalAccessException e) {  

28.     throw new InternalError(e.toString());  

29. } catch (InstantiationException e) {  

30.     throw new InternalError(e.toString());  

31. } catch (InvocationTargetException e) {  

32.     throw new InternalError(e.toString());  

33. }  

34.    }  

35.  

36. 上面的方法,除了最重要的getProxyClass,其他都很容易理解.,那麼下面開始讀getProxyClass方法  

37.  

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

39.                                         Class<?>... interfaces)  

40. throws IllegalArgumentException  

41.    {  

42. if (interfaces.length > 65535) {  

43.     throw new IllegalArgumentException("interface limit exceeded");//JDK想的果然比較到位,連interface傳的太多都想到了..~!~  

44. }  

45.  

46. Class proxyClass = null;//這個就是最後要生成的二進制碼,首先初始化一下  

47.  

48. /* collect interface names to use as key for proxy class cache */  

49. String[] interfaceNames = new String[interfaces.length];//這個存放的是對應的Interface的名字..  

50.  

51. Set interfaceSet = new HashSet(); //這個HashSet 是爲了檢測interface重複記錄的.  

52.  

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

54.     /* 

55.      * Verify that the class loader resolves the name of this 

56.      * interface to the same Class object. 

57.      */  

58.     String interfaceName = interfaces[i].getName();  

59.     Class interfaceClass = null;  

60.     try {  

61.  interfaceClass = Class.forName(interfaceName, false, loader);//創建對應接口的二進制碼,第二個參數false表示,不需要初始化  

62.     } catch (ClassNotFoundException e) {  

63.     }  

64.     if (interfaceClass != interfaces[i]) {//如果創建出來的二進制碼和原來的接口不一樣,表示這個接口對於這個classloader來說是不可見的  

65.  throw new IllegalArgumentException(  

66.      interfaces[i] + " is not visible from class loader");  

67.     }  

68.  

69.     /* 

70.      * Verify that the Class object actually represents an 

71.      * interface. 

72.      */  

73.     if (!interfaceClass.isInterface()) {//如果不是接口,那麼就拋異常,這裏就規定了,我們必須通過接口來代理..或者說,必須面向接口編程  

74.  throw new IllegalArgumentException(  

75.      interfaceClass.getName() + " is not an interface");  

76.     }  

77.  

78.     /* 

79.      * Verify that this interface is not a duplicate.前面說,InterfaceSet是拿來判斷對應的interface接口是否有重複的.這裏的方法是:在循環interfaces的時候,把每個interface都添加到interfaceSet裏..當然,在添加之前會判斷,當前循環到的接口在InterfaceSet裏是否有,如果已經有了,則拋異常,說這個接口重複了..沒有,則添加.. 

80.      */  

81.     if (interfaceSet.contains(interfaceClass)) {  

82.  throw new IllegalArgumentException(  

83.      "repeated interface: " + interfaceClass.getName());  

84.     }  

85.     interfaceSet.add(interfaceClass);  

86.  

87.     interfaceNames[i] = interfaceName;//這句就是把每個接口名放到interfaceNames數組裏..  

88. }  

89.  

90. /* 

91.  * Using string representations of the proxy interfaces as 

92.  * keys in the proxy class cache (instead of their Class 

93.  * objects) is sufficient because we require the proxy 

94.  * interfaces to be resolvable by name through the supplied 

95.  * class loader, and it has the advantage that using a string 

96.  * representation of a class makes for an implicit weak 

97.  * reference to the class. 

98.  */  

99. Object key = Arrays.asList(interfaceNames);//這裏把Interface數組轉換成list..這裏直接寫成了Object類型  

100.  

101. /* 

102.  * Find or create the proxy class cache for the class loader. 

103.  */  

104. Map cache;//放緩存的Map  

105. synchronized (loaderToCache) {//loaderToCache也是一個Map.它的key是classloader,對應的value是對應的緩存,也是一個HashMap.他把對應的不同的classloader放到loaderToCache裏,如果下次還要調用這個方法創建代理,並傳入的是同一個classloader,那麼可以直接從cache裏取..增加速度.當然,如果沒有,則創建一條記錄,放到loaderToCache裏  

106.     cache = (Map) loaderToCache.get(loader);  

107.     if (cache == null) {  

108.  cache = new HashMap();  

109.  loaderToCache.put(loader, cache);  

110.     }  

111.     /* 

112.      * This mapping will remain valid for the duration of this 

113.      * method, without further synchronization, because the mapping 

114.      * will only be removed if the class loader becomes unreachable. 

115.      */  

116. }  

117.  

118. /* 

119.  * Look up the list of interfaces in the proxy class cache using 

120.  * the key.  This lookup will result in one of three possible 

121.  * kinds of values: 

122.  *     null, if there is currently no proxy class for the list of 

123.  *         interfaces in the class loader, 

124.  *     the pendingGenerationMarker object, if a proxy class for the 

125.  *         list of interfaces is currently being generated, 

126.  *     or a weak reference to a Class object, if a proxy class for 

127.  *         the list of interfaces has already been generated. 

128.  */  

129. synchronized (cache) {  

130.     /* 

131.      * Note that we need not worry about reaping the cache for 

132.      * entries with cleared weak references because if a proxy class 

133.      * has been garbage collected, its class loader will have been 

134.      * garbage collected as well, so the entire cache will be reaped 

135.      * from the loaderToCache map. 

136.      */  

137.     do {  

138.  Object value = cache.get(key);//這裏從cache裏獲取對應的Object..第一次執行的話,明顯獲取到的是Null..key表示的是用接口名字轉換而來的list..這個可以看上面的代碼.  

139.  if (value instanceof Reference) {  

140.      proxyClass = (Class) ((Reference) value).get();//如果已經能獲取到了,那麼,我們需要的二進制碼文件就是這個獲取到的  

141.  }  

142.  if (proxyClass != null) {  

143.      // proxy class already generated: return it  

144.      return proxyClass;  

145.  } else if (value == pendingGenerationMarker) {//這裏的pendingGenerationMarker是一個靜態常量,表示 new Object().JDK給出的 解釋是,如果代理正在創建,那麼等待他  

146.      // proxy class being generated: wait for it  

147.      try {  

148.   cache.wait();  

149.      } catch (InterruptedException e) {  

150.   /* 

151.    * The class generation that we are waiting for should 

152.    * take a small, bounded time, so we can safely ignore 

153.    * thread interrupts here. 

154.    */  

155.      }  

156.      continue;  

157.  } else {  

158.      /* 

159.       * No proxy class for this list of interfaces has been 

160.       * generated or is being generated, so we will go and 

161.       * generate it now.  Mark it as pending generation. 

162.       */  

163.      cache.put(key, pendingGenerationMarker);//如果cache裏獲取到的對應於key的value是Null ,那麼,就創建一個object對象放進去.上面說了,pendingGenerationMarker= new Object();  

164.      break;  

165.  }  

166.     } while (true);  

167. }  

168.  

169. try {  

170.     String proxyPkg = null; // package to define proxy class in 這個是代理類的包名  

171.  

172.     /* 

173.      * Record the package of a non-public proxy interface so that the 

174.      * proxy class will be defined in the same package.  Verify that 

175.      * all non-public proxy interfaces are in the same package. 

176.      */  

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

178.  int flags = interfaces[i].getModifiers();//getModifiers()方法返回的是該接口的修飾類型,用Int類型表示  

179.  if (!Modifier.isPublic(flags)) {//如果不是public 的接口..  

180.  

181.  //我們可以這樣理解,我們動態創建的代理,他的修飾類型必須是和接口的修飾類型是一樣的,我們知道,接口可以是public或者默認,兩種修飾類型.這裏的判斷如果不是public接口,那麼,該接口肯定是默認的,如果是默認修飾類型,那麼,它只能被同一個包下面的類看到,所以,就必須爲該代理類創建一個包名..當然,如果是public的話,就沒必要,因爲,反正所有的類都能看到..  

182.      String name = interfaces[i].getName();  

183.      int n = name.lastIndexOf('.');  

184.      String pkg = ((n == -1) ? "" : name.substring(0, n + 1));//注意這裏的N+1 ,其實是包括 "."的..比如 com.cjb.proxy.Proxy..它返回的就是"com.cjb.proxy."注意最後的那個點  

185.  

186.      if (proxyPkg == null) {  

187.   proxyPkg = pkg;  

188.      } else if (!pkg.equals(proxyPkg)) {  

189.   throw new IllegalArgumentException(  

190.       "non-public interfaces from different packages");  

191.      }  

192.  }  

193.     }  

194.  

195.     if (proxyPkg == null) { // if no non-public proxy interfaces,這裏可以看到,如果是public 的接口,對應代理類的包名就是"",也就是沒有包名  

196.  proxyPkg = "";  // use the unnamed package  

197.     }  

198.  

199.     {  

200.  /* 

201.   * Choose a name for the proxy class to generate. 

202.   */  

203.  long num;  

204.  synchronized (nextUniqueNumberLock) {  

205.      num = nextUniqueNumber++;//Num是一個計數器.用處是,創建代理的類名的時候用..我們可以看到,它是初始值是0.然後,每被調用一次,Num++.  

206.  }  

207.  String proxyName = proxyPkg + proxyClassNamePrefix + num;//proxyPkg是之前生成的包名, proxyClassNamePrefix 是一個靜態常量,proxyClassNamePrefix="$Proxy".最後Num是計數器.也就是說,它的代理類的名字是從 $Proxy1 $Proxy2 $Proxy3一直在增長的,這樣的話,就避免了重複.  

208.  /* 

209.   * Verify that the class loader hasn't already 

210.   * defined a class with the chosen name. 

211.   */  

212.  

213.  /* 

214.   * Generate the specified proxy class.下面打紅字的兩個方法是最後生成代理對象的..但是,很悲劇的是,他是用native修飾的,也就是說,它是不是用java來實現的..也就是說,最最關鍵的地方,不是用java實現的... 

215.   */  

216.  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  

217.      proxyName, interfaces);  

218.  try {  

219.      proxyClass = defineClass0(loader, proxyName,  

220.   proxyClassFile, 0, proxyClassFile.length);  

221.  } catch (ClassFormatError e) {  

222.      /* 

223.       * A ClassFormatError here means that (barring bugs in the 

224.       * proxy class generation code) there was some other 

225.       * invalid aspect of the arguments supplied to the proxy 

226.       * class creation (such as virtual machine limitations 

227.       * exceeded). 

228.       */  

229.      throw new IllegalArgumentException(e.toString());  

230.  }  

231.     }  

232.     // add to set of all generated proxy classes, for isProxyClass  

233.     proxyClasses.put(proxyClass, null);  

234.  

235. } finally {  

236.     /* 

237.      * We must clean up the "pending generation" state of the proxy 

238.      * class cache entry somehow.  If a proxy class was successfully 

239.      * generated, store it in the cache (with a weak reference); 

240.      * otherwise, remove the reserved entry.  In all cases, notify 

241.      * all waiters on reserved entries in this cache. 

242.      */  

243.     synchronized (cache) {  

244.  if (proxyClass != null) {  

245.      cache.put(key, new WeakReference(proxyClass));  

246.  } else {  

247.      cache.remove(key);  

248.  }  

249.  cache.notifyAll();  

250.     }  

251. }  

252. return proxyClass;  

253.    }  

254.  

255. 可以看到,我們想看的,最重要的生成二進制碼的方法,是native的..讀了這麼多源碼,都是一些前期處理.關鍵的地方不知道..當然,雖然沒看到關鍵的地方,但是對於它前面的處理的學習,也是非常有用的..看看JDK是怎麼處理重名問題的,怎麼處理cache.等等..

 

 

一.相關類及其方法: 

 

java.lang.reflect.Proxy, 

Proxy 提供用於創建動態代理類和實例的靜態方法. 

newProxyInstance() 

返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序 

(詳見api文檔) 

 

java.lang.reflect.InvocationHandler, 

InvocationHandler 是代理實例的調用處理程序 實現的接口。 

invoke() 

在代理實例上處理方法調用並返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。 

(詳見api文檔) 

 

二.源代碼: 

 

被代理對象的接口及實現類:

Java代碼   

1. package com.ml.test;  

2.  

3. public interface Manager {  

4. public void modify();  

5. }  

6.  

7. package com.ml.test;  

8.  

9. public class ManagerImpl implements Manager {  

10.  

11. @Override  

12. public void modify() {  

13.   System.out.println("*******modify()方法被調用");  

14. }  

15. }  

 

業務代理類:

Java代碼   

1. package com.ml.test;  

2.  

3. import java.lang.reflect.InvocationHandler;  

4. import java.lang.reflect.Method;  

5.  

6. public class BusinessHandler implements InvocationHandler {  

7.  

8. private Object object = null;  

9.  

10. public BusinessHandler(Object object) {  

11.   this.object = object;  

12. }  

13.  

14. @Override  

15. public Object invoke(Object proxy, Method method, Object[] args)  

16.    throws Throwable {  

17.   System.out.println("do something before method");  

18.   Object ret = method.invoke(this.object, args);  

19.   System.out.println("do something after method");  

20.   return ret;  

21.  

22. }  

23. }  

 

 

 

客戶端類:

Java代碼   

1. package com.ml.test;  

2. import java.lang.reflect.Proxy;  

3. public class Client {  

4.  

5. public static void main(String[] args) {  

6.   // 元對象(被代理對象)  

7.   ManagerImpl managerImpl = new ManagerImpl();  

8.  

9.   // 業務代理類  

10.   BusinessHandler securityHandler = new BusinessHandler(managerImpl);  

11.  

12.   // 獲得代理類($Proxy0 extends Proxy implements Manager)的實例.  

13.   Manager managerProxy = (Manager) Proxy.newProxyInstance(managerImpl  

14.     .getClass().getClassLoader(), managerImpl.getClass()  

15.     .getInterfaces(), securityHandler);  

16.  

17.   managerProxy.modify();  

18. }  

19. }  

 

三.執行結果: 

do something before method 

*******modify()方法被調用 

do something after method 

 

四.機制分析: 

 

Proxy.(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下幾件事. 

(1)根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)創建代理類$Proxy. 

$Proxy0類實現了interfaces的接口,並繼承了Proxy類. 

(2)實例化$Proxy0並在構造方法中把BusinessHandler傳過去,接着$Proxy0調用父類Proxy的構造器,爲h賦值,如下:

Java代碼   

1. class Proxy{  

2.   InvocationHandler h=null;  

3.   protected Proxy(InvocationHandler h) {  

4.    this.h = h;  

5.   }  

6.   ...  

7. }  

 

 

 

 

下面是本例的$Proxy0類的源碼(好不容易纔把它提出來,改了JRE源碼,打印出字節碼,把字節碼保存爲class文件,並反編譯class文件):

Java代碼   

1. import java.lang.reflect.InvocationHandler;  

2. import java.lang.reflect.Method;  

3. import java.lang.reflect.Proxy;  

4. import java.lang.reflect.UndeclaredThrowableException;  

5.  

6. public final class $Proxy0 extends Proxy implements Manager {  

7.  

8. private static Method m1;  

9. private static Method m0;  

10. private static Method m3;  

11. private static Method m2;  

12.  

13. static {  

14.   try {  

15.    m1 = Class.forName("java.lang.Object").getMethod("equals",  

16.      new Class[] { Class.forName("java.lang.Object") });  

17.    m0 = Class.forName("java.lang.Object").getMethod("hashCode",  

18.      new Class[0]);  

19.    m3 = Class.forName("com.ml.test.Manager").getMethod("modify",  

20.      new Class[0]);  

21.    m2 = Class.forName("java.lang.Object").getMethod("toString",  

22.      new Class[0]);  

23.   } catch (NoSuchMethodException nosuchmethodexception) {  

24.    throw new NoSuchMethodError(nosuchmethodexception.getMessage());  

25.   } catch (ClassNotFoundException classnotfoundexception) {  

26.    throw new NoClassDefFoundError(classnotfoundexception.getMessage());  

27.   }  

28. }  

29.  

30. public $Proxy0(InvocationHandler invocationhandler) {  

31.   super(invocationhandler);  

32. }  

33.  

34. @Override  

35. public final boolean equals(Object obj) {  

36.   try {  

37.    return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  

38.      .booleanValue();  

39.   } catch (Throwable throwable) {  

40.    throw new UndeclaredThrowableException(throwable);  

41.   }  

42. }  

43.  

44. @Override  

45. public final int hashCode() {  

46.   try {  

47.    return ((Integer) super.h.invoke(this, m0, null)).intValue();  

48.   } catch (Throwable throwable) {  

49.    throw new UndeclaredThrowableException(throwable);  

50.   }  

51. }  

52.  

53. public final void modify() {  

54.   try {  

55.    super.h.invoke(this, m3, null);  

56.    return;  

57.   } catch (Error e) {  

58.   } catch (Throwable throwable) {  

59.    throw new UndeclaredThrowableException(throwable);  

60.   }  

61. }  

62.  

63. @Override  

64. public final String toString() {  

65.   try {  

66.    return (String) super.h.invoke(this, m2, null);  

67.   } catch (Throwable throwable) {  

68.    throw new UndeclaredThrowableException(throwable);  

69.   }  

70. }  

71. }  

 

接着把得到的$Proxy0實例強制轉換成Manager. 

當執行managerProxy.modify()方法時,就調用了$Proxy0類中的modify()方法. 

在modify方法中,調用父類Proxy中的h的invoke()方法. 

即InvocationHandler.invoke();

 

 

 

一。首先需要了解下面3個類的API:java系統支持的代理就是這3個類+反射來實現。

Java代碼   

1. import java.lang.reflect.InvocationHandler;  

2. import java.lang.reflect.Method;  

3. import java.lang.reflect.Proxy;<pre></pre>  

 

二。下面的這段測試代碼,目的就是:將一個ArrayList對象的操作進行代理,每個method前後都輸出一行字符串。

核心代碼是

Class clz = ArrayList.class;

Object proxyed_Object = Proxy.newProxyInstance(clz.getClassLoader(),

         clz.getInterfaces(), new MyInvocationHandle(new ArrayList(10)));

生成一個ArrayList的代理的對象。

Java代碼   

1. package proxy;  

2.  

3. import java.lang.reflect.InvocationHandler;  

4. import java.lang.reflect.Method;  

5. import java.lang.reflect.Proxy;  

6. import java.util.ArrayList;  

7. import java.util.List;  

8.  

9. /** 

10. * DEMO:測試java中的代理 

11. *  

12. * 代理了一個ArrayList對象,並在調用方法前後各加了一個systemout輸出 

13. * @author wei.songw 

14. *  

15. */  

16. public class MyInvocationHandle implements InvocationHandler {  

17.  

18.    //對代理對象的引用.  

19.    private List aList;  

20.  

21.    /** 

22.     * 構造器。 

23.     * @param list 代理對象 

24.     */  

25.    public MyInvocationHandle(Object list) {  

26.        this.aList = (List) list;  

27.    }  

28.  

29.    /** 

30.     * InvocationHandler的方法實現 

31.     */  

32.    @Override  

33.    public Object invoke(Object proxy, Method method, Object[] args)  

34.            throws Throwable {  

35.          

36.        //方法前調用,插入一段消息  

37.        System.out.println("before : "+method.getName());  

38.          

39.        //調用方法.  

40.        Object object = method.invoke(aList, args);  

41.          

42.        //方法後調用,插入一段消息  

43.        System.out.println("after : "+method.getName());  

44.          

45.        return object;  

46.    }  

47.  

48.  

49.  

50.    public static void main(String[] args) {  

51.        //需要代理一個ArrayList對象,因此按照API構造一個Proxy對象  

52.        //,同時也初始化了處理Proxy的MyInvocationHandle對象  

53.        Class clz = ArrayList.class;  

54.        Object proxyed_Object = Proxy.newProxyInstance(clz.getClassLoader(),  

55.                clz.getInterfaces(), new MyInvocationHandle(new ArrayList(10)));  

56.          

57. //TEST1:查看一下代理生成類的接口???  

58. //      Class<!---->[] itfs = proxyed_Object.getClass().getInterfaces();  

59. //      for (int i = 0; i < itfs.length; i++) {  

60. //          System.out.println(itfs[i].getName());  

61. //      }  

62.          

63.        //注意!這裏操作的是代理類!  

64.        List list = (List)proxyed_Object;  

65.        list.add(Integer.valueOf(10));  

66.          

67. //TEST2:輸出一下list的大小,確認add方法被調用        

68. //      System.out.println(list.size());  

69.    }  

70. }  

71. <pre></pre>  

輸出如下:

before : add

after : add

如果將TEST2段代碼去掉註釋,可以看到如下輸出:

before : add

after : add

before : size

after : size

1

證明add方法確實被調用了。

 

 三。代理模式的結構圖(UML+時序圖)

 

通過UML圖可以看到代理類和被代理實際對象實現同一接口/或者一個抽象類,因此外部調用Subject,是感覺不到代理類的存在。

 

問題出現了:上面的測試代碼中,被代理ArrayList對象,並未與調用者MyInvocationHandle實現同樣的接口 。那是怎麼實現代理模式的呢?

看Proxy類的文檔,寫到:

Java代碼   

1. /** 

2.  * Returns an instance of a proxy class for the specified interfaces 

3.  * that dispatches method invocations to the specified invocation 

4.  * handler.  This method is equivalent to: 

5.  * <pre>     *     Proxy.getProxyClass(loader, interfaces). 

6.  *         getConstructor(new Class[] { InvocationHandler.class }). 

7.  *         newInstance(new Object[] { handler }); 

8.  * </pre> 

9.  * 

10.  * <p><code>Proxy.newProxyInstance</code> throws 

11.  * <code>IllegalArgumentException</code> for the same reasons that 

12.  * <code>Proxy.getProxyClass</code> does. 

13.  * 

14.  * @param  loader the class loader to define the proxy class 

15.  * @param  interfaces the list of interfaces for the proxy class 

16.  *     to implement 

17.  * @param   h the invocation handler to dispatch method invocations to 

18.  * @return a proxy instance with the specified invocation handler of a 

19.  *     proxy class that is defined by the specified class loader 

20.  *     and that implements the specified interfaces 

21.  * @throws IllegalArgumentException if any of the restrictions on the 

22.  *     parameters that may be passed to <code>getProxyClass</code> 

23.  *     are violated 

24.  * @throws NullPointerException if the <code>interfaces</code> array 

25.  *     argument or any of its elements are <code>null</code>, or 

26.  *     if the invocation handler, <code>h</code>, is 

27.  *     <code>null</code> 

28.  */</p><pre></pre><p></p>  

返回一個實現指定接口的代理類實例,並綁定方法調用到一個指定的invocation handler.

如果將TEST1段註釋去掉,可以看到這個代理對象實現瞭如下接口:

 java.util.List    java.util.RandomAccess     java.lang.Cloneable      java.io.Serializable

 

並且Proxy.newProxyInstance等同於

     *     Proxy.getProxyClass(loader, interfaces).

     *         getConstructor(new Class[] { InvocationHandler.class }).

     *         newInstance(new Object[] { handler });

這裏是部分源碼:

Java代碼   

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

2. int flags = interfaces[i].getModifiers();  

3. if (!Modifier.isPublic(flags)) {  

4.    String name = interfaces[i].getName();  

5.    int n = name.lastIndexOf('.');  

6.    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  

7.    if (proxyPkg == null) {  

8.    proxyPkg = pkg;  

9.    } else if (!pkg.equals(proxyPkg)) {  

10.    throw new IllegalArgumentException(  

11.        "non-public interfaces from different packages");  

12.    }  

13. }  

14.   }<pre></pre>  

 

 

通過時序圖可以看到,外部Client和代理類打交道,而代理類在調用實際對象時可以增加一些有益的操作。

 

 

 

Java代碼   

1. /** 

2. * JDK 動態代理類分析(java.lang.reflect.Proxy使用) 

3. *  

4. * @author 張明學 

5. *  

6. */  

7. public class ProxyStudy {  

8.      

9.    @SuppressWarnings("unchecked")  

10.    public static void main(String[] args) throws Exception {  

11.        // 動態代理類:通用指定類加載器,和接口產生一類  

12.        // getProxyClass()返回代理類的 java.lang.Class 對象,並向其提供類加載器和接口數組。  

13.        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  

14.        System.out.println("動態產生的類名爲:" + clazzProxy.getName());  

15.        System.out.println("----------獲取動態產生的類的構造方法---------");  

16.        Constructor[] constructors = clazzProxy.getConstructors();  

17.        int i = 1;  

18.        for (Constructor constructor : constructors) {  

19.            System.out.println("第" + (i++) + "個構造方法名:" + constructor.getName());  

20.            Class[] parameterClazz = constructor.getParameterTypes();  

21.            System.out.println("第" + (i++) + "個構造方法參數:" + Arrays.asList(parameterClazz));  

22.        }  

23.        System.out.println("----------獲取動態產生的類的普通方法---------");  

24.        Method[] methods = clazzProxy.getDeclaredMethods();  

25.        for (int j = 0; j < methods.length; j++) {  

26.            Method method = methods[j];  

27.            System.out.println("第" + (j + 1) + "個普通方法名:" + method.getName());  

28.            Class[] parameterClazz = method.getParameterTypes();  

29.            System.out.println("第" + (j + 1) + "個普通方法參數:" + Arrays.asList(parameterClazz));  

30.        }  

31.        System.out.println("---------獲取動態代理對象的構造方法---------");  

32.        // 動態代理產生的對象的構造方法需要一個實現java.lang.reflect.InvocationHandler接口的對象,故不能通過  

33.        // clazzProxy.newInstance();產生一個對象,可以根據構造方法產生一個對象  

34.        // InvocationHandler 是代理實例的調用處理程序 實現的接口。  

35.        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);  

36.  

37.        // 代理產生的對象  

38.        Collection proxyBuildCollection = (Collection) constructor  

39.                .newInstance(new InvocationHandler() {  

40.                    // 爲什麼這裏選擇ArrayList作爲目標對象?  

41.                    // 因爲這裏的constructor是clazzProxy這個動態類的構造方法,clazzProxy是通過Proxy.getProxyClass()方法產生的,  

42.                    // 該方法有兩個參數,一個是指定類加載器,一個是指定代理要實現的接口,這個接口我上面指定了Collection  

43.                    // 而ArrayList實現了Collection接口,固可以爲該動態類的目標對象  

44.                    ArrayList target = new ArrayList();// 動態類的目標對象  

45.  

46.                    public Object invoke(Object proxy, Method method,  

47.                            Object[] args) throws Throwable {  

48.                        System.out.println("執行目標" + method.getName() + "方法之前:"  

49.                                + System.currentTimeMillis());  

50.                        Object result = method.invoke(target, args);// 其實代理對象的方法調用還是目標對象的方法  

51.                        System.out.println("執行目標" + method.getName() + "方法之後:"  

52.                                + System.currentTimeMillis());  

53.                        return result;  

54.                    }  

55.  

56.                });  

57.        proxyBuildCollection.clear();  

58.        proxyBuildCollection.add("abc");  

59.        proxyBuildCollection.add("dbc");  

60.        System.out.println(proxyBuildCollection.size());  

61.        System.out.println(proxyBuildCollection.getClass().getName());  

62.          

63.        /** 

64.         * 動態代理:總結如下: 

65.         * 1,通過Proxy.getProxyClass(classLoader,interface)方法產生一個動態類的class字節碼(clazz) 

66.         *    該getProxyClass()方法有兩個參數:一個是指定該動態類的類加載器,一個是該動態類的要實現的接口(從這裏可以看現JDK的動態代理必須要實現一個接口) 

67.         *     

68.         * 2,通過第一步的獲取的clazz對象可以獲取它的構造方法constructor,那麼就可以通用constructor的newInstance()方法構造出一個動態實體對象 

69.         *    但constructor的newInstance()方法需要指定一個實現了InvocationHandler接口的類handler,在該類中需要一個目標對象A和實現invoke方法 

70.         *    目標對象A要求能對第一步中的接口的實現,因爲在invoke方法中將會去調用A中的方法並返回結果。 

71.         *    過程如下:調用動態代理對象ProxyObject的x方法 ————> 進入構造方法傳進的handler的invoke方法 ————> invoke方法調用handler中的target對象 

72.         *            的x方法(所以要求target必須要實現構造動態代理類時指定的接口)並返回它的返回值。(其實如果我們代理P類,那麼target就可以選中P類,只是要求P必需實現一個接口) 

73.         *     

74.         *    那麼上述中x方法有哪些呢?除了從Object繼承過來的方法中除toString,hashCode,equals外的方法不交給handler外,其它的方法全部交給handler處理 

75.         *    如上面proxyBuildCollection.getClass().getName()就沒有調用handler的getClass方法,而是調用自己的 

76.         *     

77.         * 3,在handler的invoke方法中return method.invoke(target,args)就是將方法交給target去完成。那麼在這個方法執行之前,之後,異常時我們都可以做一些操作, 

78.         *    並且可以在執行之前檢查方法的參數args,執行之後檢查方法的結果 

79.         */  

80.        System.out.println("-------------------下面的寫法更簡便--------------------");  

81.          

82.        // proxyBuildColl是對ArrayList進行代理  

83.        Collection proxyBuildCollection2 = (Collection) Proxy.newProxyInstance(  

84.                Collection.class.getClassLoader(),// 指定類加載器  

85.                new Class[] { Collection.class },// 指定目標對象實現的接口  

86.                // 指定handler  

87.                new InvocationHandler() {  

88.                    ArrayList target = new ArrayList();  

89.  

90.                    public Object invoke(Object proxy, Method method,  

91.                            Object[] args) throws Throwable {  

92.                        System.out.println(method.getName() + "執行之前...");  

93.                        if (null != args) {  

94.                            System.out.println("方法的參數:" + Arrays.asList(args));  

95.                        } else {  

96.                            System.out.println("方法的參數:" + null);  

97.                        }  

98.                        Object result = method.invoke(target, args);  

99.                        System.out.println(method.getName() + "執行之後...");  

100.                        return result;  

101.                    }  

102.                });  

103.        proxyBuildCollection2.add("abc");  

104.        proxyBuildCollection2.size();  

105.        proxyBuildCollection2.clear();  

106.        proxyBuildCollection2.getClass().getName();  

107.          

108.        System.out.println("-------------------對JDK動態代理的重構--------------------");  

109.        Set proxySet = (Set) buildProxy(new HashSet(), new MyAdvice());  

110.        proxySet.add("abc");  

111.        proxySet.size();  

112.    }  

113.    /** 

114.     * 構造一個目標對象的代理對象 

115.     *  

116.     * @param target 

117.     *            目標對象(需要實現某個接口) 

118.     * @return 

119.     */  

120.    public static Object buildProxy(final Object target,final AdviceInter advice) {  

121.        Object proxyObject = Proxy.newProxyInstance(  

122.                target.getClass().getClassLoader(),// 指定類加載器  

123.                target.getClass().getInterfaces(), // 指定目標對象實現的接口  

124.                // handler  

125.                new InvocationHandler() {  

126.                      

127.                    public Object invoke(Object proxy, Method method,  

128.                            Object[] args) throws Throwable {  

129.                        advice.beforeMethod(target, method, args);  

130.                        Object result = method.invoke(target, args);  

131.                        advice.afterMethod(target, method, args);  

132.                        return result;  

133.                    }  

134.                });  

135.        return proxyObject;  

136.    }  

137.      

138. }  

 

Java代碼   

1. /** 

2. * 代理中執行目標方法之前之後的操作的一個實例 

3. *  

4. * @author 張明學 

5. *  

6. */  

7. public class MyAdvice implements AdviceInter {  

8.  

9.    public void afterMethod(Object target, Method method, Object[] args) {  

10.        System.out.println("目標對象爲:" + target.getClass().getName());  

11.        System.out.println(method.getName() + "執行完畢!");  

12.    }  

13.  

14.    public void beforeMethod(Object target, Method method, Object[] args) {  

15.        System.out.println(method.getName() + "開始執行");  

16.        if (null != args) {  

17.            System.out.println("參數爲:" + Arrays.asList(args));  

18.        } else {  

19.            System.out.println("參數爲:" + null);  

20.        }  

21.    }  

22. }  

 

Java代碼   

1. /** 

2. * 代理中執行目標方法之前之後的操作 

3. *  

4. * @author 張明學 

5. *  

6. */  

7. public interface AdviceInter {  

8.    /** 

9.     * 目標方法執行之前 

10.     *  

11.     */  

12.    public void beforeMethod(Object target, Method method, Object[] args);  

13.  

14.    /** 

15.     * 目標方法執行之後 

16.     *  

17.     * @param target 

18.     *            目標對象 

19.     * @param method 

20.     *            方法 

21.     * @param args 

22.     *            參數 

23.     */  

24.    public void afterMethod(Object target, Method method, Object[] args);  

25. }  

 

 

 

介紹       

代理模式 

        代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。

按照代理的創建時期,代理類可以分爲兩種:

靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。

動態代理:在程序運行時,運用反射機制動態創建而成。 

        在JDK 1.3以後提供了動態代理的技術,允許開發者在運行期創建接口的代理實例。 JDK的動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。其中InvocationHandler是一個接口,可以通過實現該接口定義橫切邏輯,在並通過反射機制調用目標類的代碼,動態將橫切邏輯和業務邏輯編織在一起。

一)靜態代理: 

1.Count.java

[java] view plaincopy

1. <span style="font-size:16px;">package net.battier.dao;    

2. /**   

3. * 定義一個賬戶接口   

4. *    

5. * @author Administrator   

6. *    

7. */     

8. public interface Count {     

9.    // 查看賬戶方法     

10.    public void queryCount();     

11.    // 修改賬戶方法     

12.    public void updateCount();      

13. }   </span>  

2.CountImpl.java

[java] view plaincopy

1. package net.battier.dao.impl;     

2. import net.battier.dao.Count;     

3. /**   

4. * 委託類(包含業務邏輯)   

5. *    

6. * @author Administrator   

7. *    

8. */     

9. public class CountImpl implements Count {  

10.    @Override     

11.    public void queryCount() {     

12.        System.out.println("查看賬戶方法...");     

13.    }     

14.    @Override     

15.    public void updateCount() {     

16.        System.out.println("修改賬戶方法...");     

17.    }     

18. }     

3.CountProxy.java  

[java] view plaincopy

1. package net.battier.dao.impl;     

2.     

3. import net.battier.dao.Count;     

4.     

5. /**   

6. * 這是一個代理類(增強CountImpl實現類)   

7. *    

8. * @author Administrator   

9. *    

10. */     

11. public class CountProxy implements Count {     

12.    private CountImpl countImpl;     

13.     

14.    /**   

15.     * 覆蓋默認構造器   

16.     *    

17.     * @param countImpl   

18.     */     

19.    public CountProxy(CountImpl countImpl) {     

20.        this.countImpl = countImpl;     

21.    }     

22.     

23.    @Override     

24.    public void queryCount() {     

25.        System.out.println("事務處理之前");     

26.        // 調用委託類的方法;     

27.        countImpl.queryCount();     

28.        System.out.println("事務處理之後");     

29.    }     

30.     

31.    @Override     

32.    public void updateCount() {     

33.        System.out.println("事務處理之前");     

34.        // 調用委託類的方法;     

35.        countImpl.updateCount();     

36.        System.out.println("事務處理之後");     

37.     

38.    }     

39.     

40. }  

4.TestCount.java

[java] view plaincopy

1. Java代碼   

2. package net.battier.test;     

3. import net.battier.dao.impl.CountImpl;     

4. import net.battier.dao.impl.CountProxy;     

5. /**   

6. *測試Count類   

7. *    

8. * @author Administrator   

9. *    

10. */     

11. public class TestCount {     

12.    public static void main(String[] args) {     

13.        CountImpl countImpl = new CountImpl();     

14.        CountProxy countProxy = new CountProxy(countImpl);     

15.        countProxy.updateCount();     

16.        countProxy.queryCount();     

17.    }     

18. }    

       觀察代碼可以發現每一個代理類只能爲一個接口服務,這樣一來程序開發中必然會產生過多的代理,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是重複代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那麼此時就必須使用動態代理完成。 

 

 

二)動態代理

1.定義一個接口和實現類: 

[java] view plaincopy

1. <span style="font-weight: normal;"><span style="font-size:16px;">package com.tech.service;  

2. public interface PersonService {  

3.    public String getPersonName(Integer personId);  

4.    public void save(String name);  

5.    public void update(Integer personId, String name);  

6. }  

7.  

8. package com.tech.service.impl;  

9. import com.tech.service.PersonService;  

10. public class PersonServiceBean implements PersonService {     

11.    public String user = null;  

12.  

13.    public PersonServiceBean(){};  

14.    public PersonServiceBean(String user){  

15.        this.user = user;  

16.    }  

17.      

18.    @Override  

19.    public String getPersonName(Integer personId) {  

20.        // TODO Auto-generated method stub  

21.        System.out.println("這是find方法");  

22.        return this.user;  

23.    }  

24.  

25.    @Override  

26.    public void save(String name) {  

27.        // TODO Auto-generated method stub  

28.        System.out.println("這是save方法");  

29.    }  

30.  

31.    @Override  

32.    public void update(Integer personId, String name) {  

33.        // TODO Auto-generated method stub  

34.        System.out.println("這是update方法");  

35.    }  

36.    public String getUser() {  

37.        return user;  

38.    }  

39.    public void setUser(String user) {  

40.        this.user = user;  

41.    }  

42.  

43. }</span></span>  

2.JDK動態代理代理類

[java] view plaincopy

1. package com.tech.jdkproxy;  

2.  

3. import java.lang.reflect.InvocationHandler;  

4. import java.lang.reflect.Method;  

5. import java.lang.reflect.Proxy;  

6.  

7. import com.tech.service.impl.PersonServiceBean;  

8.  

9. /** 

10. *   

11. * 切面   

12. * @author ch 

13.

14. */  

15. public class JDKProxyFactory implements InvocationHandler{  

16.  

17.    private Object proxyObject; //目標對象  

18.  

19.    /** 

20.     * 綁定委託對象並返回一個代理類  

21.     * @param proxyObject 

22.     * @return 

23.     */  

24.    public Object createProxyInstance(Object proxyObject) {  

25.        this.proxyObject = proxyObject;  

26.          

27.        //生成代理類的字節碼加載器   

28.        ClassLoader classLoader = proxyObject.getClass().getClassLoader();  

29.        //需要代理的接口,被代理類實現的多個接口都必須在這裏定義  (這是一個缺陷,cglib彌補了這一缺陷)    

30.        Class<?>[] proxyInterface = proxyObject.getClass().getInterfaces();//new Class[]{};   

31.          

32.        //織入器,織入代碼並生成代理類     

33.        return Proxy.newProxyInstance(classLoader,  

34.                proxyInterface, this);  

35.  

36.    }  

37.  

38.    @Override  

39.    public Object invoke(Object proxy, Method method, Object[] args)  

40.            throws Throwable {  

41.        PersonServiceBean bean = (PersonServiceBean)this.proxyObject;  

42.        Object result = null;  

43.        //控制哪些用戶執行切入邏輯  

44.        if(bean.getUser() != null) {  

45.            //執行原有邏輯     

46.            result = method.invoke(this.proxyObject, args);  

47.        }  

48.        return result;  

49.    }  

50. }  

3.測試類

[java] view plaincopy

1. package com.tech.junit;  

2.  

3. import org.junit.BeforeClass;  

4. import org.junit.Test;  

5.  

6. import com.tech.jdkproxy.JDKProxyFactory;  

7. import com.tech.service.PersonService;  

8. import com.tech.service.impl.PersonServiceBean;  

9.  

10. public class PersonTest {  

11.  

12.    @BeforeClass  

13.    public static void setUpBeforeClass() throws Exception {  

14.    }  

15.  

16.    @Test  

17.    public void Test() {  

18.        JDKProxyFactory factory = new JDKProxyFactory();  

19.        PersonService bean = (PersonService) factory  

20.                .createProxyInstance(new PersonServiceBean("lucy"));  

21.        //用戶爲lucy,有權限  

22.        bean.save("abc");  

23.          

24.        PersonService bean2 = (PersonService) factory  

25.            .createProxyInstance(new PersonServiceBean());  

26.        //用戶爲null,沒有權限,不輸出  

27.        bean2.save("abc");  

28.    }  

29. }  

 

 

JDK的動態代理深入解析(Proxy,InvocationHandler)-I

1.   DK首先定義一個接口(動態代理就是建立在接口編程上,這個接口就是被代理對象)

public interface UserDao {

    public void save(User user);

    public void delete(User user);

    public void update(User user);

}

2.   接口的實現類(真實業務的實現)

public class UserDaoImpl implements UserDao {

    public void delete(User user) {

       System.out.println("刪除用戶成功。。。。");

    }

    public void save(User user) {

       System.out.println("插入用戶成功。。。。");

    }

    public void update(User user) {

       System.out.println("更新用戶成功。。。。");

    }

}

3.   實現InVocationHandler接口的invoke()方法

public class LogHandler_old implements InvocationHandler {

    // 組合的方式引入被代理對象

    private Object target = null;

    private static Logger logger = Logger.getLogger(LogHandler_old.class);

 

    // 構造函數注入被代理對象

    public LogHandler_old(Object target) {

       this.target = target;

    }

    /*

    * invoke 必須根據需要 Override

    * Proxy.newProxyInstance(...)時候會自動調用這個invoke方法

    */

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

           throws Throwable {

       logger.info(method.getName() + "開始執行。。。。");

       Object result = method.invoke(target, args);

       logger.info(method.getName() + "執行結束。。。。");

       return result;

    }

}

 

4.   創建Proxy對象,測試

public class ProxyTest_old {

    public static void main(String[] args) {

       UserDao userDao = new UserDaoImpl();

       LogHandler_old logHandler = new LogHandler_old(userDao);

       UserDao userDaProxy = (UserDao) Proxy.newProxyInstance(userDao

              .getClass().getClassLoader(), userDao.getClass()

              .getInterfaces(), logHandler);

       userDaProxy.delete(new User());

       userDaProxy.save(new User());

       userDaProxy.update(new User());

    }

}

解釋:

1.    Proxy即動態代理類;

2.    Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,返回後的代理類可以當作被代理類使用;

它有三個參數:

ClassLoader loader   ----指定被代理對象的類加載器

Class[] Interfaces   ----指定被代理對象所以事項的接口

InvocationHandler h ----指定需要調用的InvocationHandler對象

3.    實現InVocationHandler接口的LogHandler_old對象

這個對象的invoke()方法就是Proxy這個動態代理類所代理的接口類的抽象方法的真實實現;

它有三個參數:

Object proxy         -----代理類對象

Method method        -----被代理對象的方法(這裏不是接口的抽象方法了,是具體的實現類中的方法)

Object[] args        -----該方法的參數數組

JDK中具體的動態代理類是怎麼產生的呢?

1.產生代理類$Proxy0類

執行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

將產生$Proxy0類,它繼承Proxy對象,並根據第二個參數,實現了被代理類的所有接口,自然就可以生成接口要實現的所有方法了(這時候會重寫hashcode,toString和equals三個方法),但是還沒有具體的實現體;

2.   將代理類$Proxy0類加載到JVM中

這時候是根據Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一個參數----就是被代理類的類加載器,把當前的代理類加載到JVM中

3.   創建代理類$Proxy0類的對象

調用的$Proxy0類的$Proxy0(InvocationHandler)構造函數,生成$Proxy0類的對象

參數就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三個參數

這個參數就是我們自己實現的InvocationHandler對象,我們知道InvocationHandler對象中組合加入了代理類代理的接口類的實現類;所以,$Proxy0對象調用所有要實現的接口的方法,都會調用InvocationHandler對象的invoke()方法實現;

4.   生成代理類的class byte

動態代理生成的都是二進制class字節碼

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