實現生成代理類的原理

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

java.lang.reflect.Proxy 1.調用Proxy類的Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法
1.1調用Proxy類的getProxyClass0(ClassLoader loader, Class<?>... interfaces)方法:定義一個代理類,它將執行訪問檢查,如Class.forName(VM將調用ClassLoader.
checkPackageAccess):a.“getClassLoader”權限檢查loader == null b.在它實現的接口上執行checkPackageAccess。
要獲取一個代理類的構造函數和新實例,它執行在它實現的接口上的包訪問檢查,如在Class.
getConstructor中。如果接口是非公共的,代理類必須由定義接口的定義加載器。 如果調用者的類加載器不同於接口的定義加載器,VM將在生成的代理類時拋出IllegalAccessError通過defineClass0方法定義。
1.1.1調用System類的getSecurityManager()如果已經爲當前應用建立了安全管理器,則返回該安全管理器; 否則返回null。
1.1.3調用checkProxyLoader(ClassLoader ccl, ClassLoader loader)來判斷loader是否爲null
1.1.4調用ReflectUtil類的checkProxyPackageAccess(ClassLoader ccl, Class<?>[] interfaces)
1.1.5new String[interfaces.length];收集目標類實現的接口名稱以用作代理類高速緩存的鍵
1.1.6遍歷接口名稱數組,對數組元素分別調用Class.forName(String name, boolean initialize,ClassLoader loader) 返回指定name字符串名稱的類或接口相關聯的Class對象 :使用給定的類加載器返回與具有給定字符串名稱的類或接口相關聯的Class對象。給出類或接口的完全限定名(以getName返回的相同格式),此方法嘗試查找,加載和鏈接的類或接口。 指定的類加載器用於加載類或接口。 如果參數加載器爲null,則通過引導類加載器加載類。 僅當initialize參數爲true並且尚未初始化時才初始化該類。
1.1.7if (!interfaceClass.isInterface())判斷調用Class.forName()返回的Class對象是否爲接口類型,false則拋出異常
1.1.8將interfaceClass add進入Set interfaceSet中,將interfaceNames 添加到String[] interfaceNames中
1.1.9執行Object key = Arrays.asList(interfaceNames);返回可序列化的接口名稱的列表。此處序列化接口名稱的列表,用於使用代理接口的字符串表示作爲代理類緩存中的鍵(而不是它們的Class對象)是足夠的,因爲我們需要通過提供的類加載器通過名稱來解析代理接口,並且它具有使用字符串表示的類使得對類的隱式弱引用。
1.1.10執行Map loaderToCache.put(ClassLoder loader,Map cache),爲類加載器查找或創建代理類緩存cache,把類加載器和類緩存cache作爲鍵值對添加到loaderToCache中,loaderToCache用於存放類加載器和該加載器的代理類緩存的映射。
1.1.11執行Object value = cache.get(key)判斷加載器的代理類緩存Map cache中是否存在以'接口名稱的列表'key爲鍵值的value(使用鍵查找代理類緩存map中的接口列表)。
synchronized (cache) {
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.
*/
//當第一個Thread進入同步塊中時,發現key還沒有生成對應的弱引用,則首先把key添加到緩存中並標誌此時key的狀態爲pendingGenerationMarker(當前正在生成特定的代理類),然後跳出循環。接下來第二個Thread進入同步代碼塊後發現key已經被標誌爲pendingGenerationMarker狀態,則Thread進入等待wait狀態.
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
此查找將導致三種可能的值中的一種:
a.當獲取key對應的value已經存在引用了(即代理類已經生成了),則直接返回這個key對應的代理類;
b.當獲取key對應的value不存在引用且value被標記爲當前正在生成特定的代理類時,線程wait一段時間以等待直到代理類生成結束;
c.這個接口列表的代理類沒有生成或正在生成,所以我們會去現在生成它,將其標記爲待處理代。
Map cache 用於存儲以‘接口列表名稱’爲key,以‘當前key對應的代理類生成的狀態’爲value的集合,key對應的代理類生成的狀態(即value的值主要包含兩種狀態:a.pendingGenerationMarkerr(當前正在生成特定的代理類)&&b.已經完成key對應的代理類生成(即已獲取代理類的弱引用))
1.1.12執行byte[] proxyClassFile =ProxyGenerator .generateProxyClass(proxyName, interfaces),生成一個給定名稱proxyName和代理接口列表interfaces的代理類。
1.1.12.1執行ProxyGenerator gen = new ProxyGenerator(name, interfaces),構造ProxyGenerator以生成具有指定名稱和給定接口的代理類。 ProxyGenerator對象包含正在生成特定代理類的狀態。ProxyGenerator包含用於爲java.lang.reflect.Proxy生成動態代理類的代碼。
ProxyGenerator.java
1.1.12.1.1執行ProxyGenerator.java的構造函數
/** name of the superclass of proxy classes */
private final static String superclassName = "java/lang/reflect/Proxy";
private List<MethodInfo> methods = new ArrayList<MethodInfo>();
private Map<String, List<ProxyMethod>> proxyMethods =new HashMap<String,List<ProxyMethod>>();
private String className; /** name of proxy class */
private Class[] interfaces; /** proxy interfaces */
private ProxyGenerator(String className, Class[] interfaces) {
this.className = className;
this.interfaces = interfaces;
}
                 1.1.12.2執行final byte[] classFile = gen.generateClassFile();調用ProxyGenerator類的generateClassFile()生成代理類生成class類文件。 此方法驅動類文件生成過程。
1.1.12.2.1執行以下代碼塊,循環遍歷目標類實現的接口名稱對應的Class對象,調用Class.getMethods()方法返回該接口的Class對象的所有Method對象數組。並調用ProxyGenerator類的addProxyMethod(Method m, Class fromClass)方法,將目標類實現的接口中的每一個方法添加到proxyMethods 集合中。目標是爲代理類添加每一個目標類實現的接口對象中的方法。
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
1.1.12.2.1.1調用addProxyMethod(Method m, Class fromClass)方法,執行以下代碼塊完成“添加另一個要被代理的方法,通過創建一個新的ProxyMethod對象或者爲一個重複的方法增加一箇舊的方法”的功能:
String sig = name + getParameterDescriptors(parameterTypes);
proxyMethods.put(sig, sigmethods);
1.1.12.2.2執行methods.add(generateConstructor());返回一個MethodInfo對象(一個生成代理類的構造方法),並將其add到 List<MethodInfo> methods集合中去,methods集合用於存儲生成的代理類的方法。MethodInfo對象包含有關正在生成的類中的特定方法的信息。MethodInfo類爲ProxyGenerator類中聲明的內部類。
1.1.12.2.2.1執行generateConstructor()方法中的代碼塊:
MethodInfo minfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", ACC_PUBLIC);
DataOutputStream out = new DataOutputStream(minfo.code);
out.writeShort(cp.getMethodRef(superclassName,"<init>", (Ljava/lang/reflect/InvocationHandler;)V"));
1.1.12.2.3執行以下代碼塊,遍歷Map<String, List<ProxyMethod>> proxyMethods 集合,執行methods.add(pm.generateMethod());生成代理類的代理方法(該代理方法爲目標類實現的接口中的方法的代理方法及其他一些方法)。
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,"Ljava/lang/reflect/Method;",ACC_PRIVATE | ACC_STATIC));
methods.add(pm.generateMethod()); // generate code for proxy method and add it
}
}
1.1.12.2.3.1調用ProxyMethod類的generateMethod()方法,返回此方法的MethodInfo對象,包括生成代碼和異常表條目。由於是生成代理方法,那麼猜想方法參數中必須要包含InvocationHandler,且方法體中必須要調用InvocationHandler的invoke()方法,這樣才能實現:使用代理類調用目標類的接口方法時,回調InvocationHandler的invoke()方法。查看以下代碼可以發現猜想正確:
out.writeShort(cp.getFieldRef(superclassName,handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));
out.writeShort(cp.getFieldRef(dotToSlash(className),methodFieldName, "Ljava/lang/reflect/Method;"));
out.writeShort(cp.getInterfaceMethodRef(
"java/lang/reflect/InvocationHandler",
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;"));
1.1.12.2.4執行new DataOutputStream(ByteArrayOutp utStream).writeShort(cp.getClass(superclassName));將代理類的超級父類'java/lang/reflect/Proxy'寫入到代理類的class文件。以下代碼段爲Proxy類的部分代碼:
public class Proxy{
protected InvocationHandler h;
private Proxy() {}
protected Proxy(InvocationHandler h) {
doNewInstanceCheck();
this.h = h;
}
}
1.1.12.2.5最後將ByteArrayOutputStream.toByteArray()創建一個新分配的 byte 數組。其大小是此輸出流的當前大小,並且緩衝區的有效內容已複製到該數組中。並將此包含代理類class文件信息的byte數組返回。
1.1.12.3執行FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class");file.write(classFile);file.close();把生成的類文件寫入到轉換爲使用“/”作爲包分隔符的完全限定類名,在類文件格式中使用的表示。(把完全限定類名的.轉換爲/)
1.1.13調用native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len),生成指定的代理類
1.1.14執行proxyClasses.put(proxyClass, null),把生成的代理類添加到所有生成的代理類的集合中
1.1.15執行 cache.put(key, new WeakReference(proxyClass));如果成功生成了代理類,則將其存儲在緩存中(使用弱引用)。
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
如果成功生成了代理類,則將其存儲在緩存中(使用弱引用); 否則,刪除代理類緩存條目的“未決生成”狀態保留的條目。 在所有情況下,通知所有服務器此緩存中保留的條目。
1.2調用cl.getConstructor(constructorParams),其中constructorParams的定義爲:private final static Class[] constructorParams ={ InvocationHandler.class };完成獲取參數爲InvocationHandler.class類型參數的構造函數。
1.3調用Proxy.newInstance(Constructor<?> cons, InvocationHandler h)通過指定的InvocationHandler對象爲參調用構造函數創建代理實例
1.3.1調用Constructor.newInstance(Object ... initargs)使用由此構造函數對象表示的構造函數並傳入指定的初始化參數創建和初始化構造函數的聲明類的新實例。單獨的參數被自動展開以匹配原始形式參數,並且原始和參考參數都根據需要進行方法調用轉換。傳入的參數爲InvocationHandler 實例對象,則調用該以InvocationHandler 實例對象爲參的構造函數返回代理類實例。
代碼剖析總結1:
在1.1.12.2.2步驟中爲代理類的class文件創建了一個參數爲InvocationHandler 類型對象的構造函數,此參數在代理類創建時傳入。在步驟1.1.12.2.3.1中調用ProxyMethod類的generateMethod()方法,用於生成代理方法,該方法參數中包含InvocationHandler,且方法體調用InvocationHandler的invoke()方法,來實現“在使用代理類調用目標類的接口方法時,通過代理類中的InvocationHandler對象回調InvocationHandler的invoke()方法”。在步驟1.1.12.2.4中指明瞭Proxy爲代理類的超類(注:關鍵部分生成代理類class文件的方法可以查看ProxyGenerator.generateClassFile()方法)

代碼剖析總結2:
源碼中非常重處要的兩synchronized同步代碼塊:
第一個同步代碼塊中主要完成如下工作:
在多線程併發生成代理類時,當第一個Thread1進入同步塊中時,發現key還沒有生成對應的弱引用,則首先把key添加到緩存中並標誌此時key的狀態爲pendingGenerationMarker(當前正在生成特定的代理類),然後跳出循環,走出同步代碼塊。接下來Thread2進入同步代碼塊後發現key已經被標誌爲pendingGenerationMarker狀態,則Thread2進入等待wait狀態。
第二個同步代碼塊中主要完成如下工作:
Thread1進入第二個同步代碼塊中時,判斷是否完成key的代理類生成工作,如果完成則將key和其生成的代理類的弱引用存儲到緩存cache中(即改變key的狀態),如果沒有完成key的代理類生成工作,則刪除代理類緩存條目的“未決生成”狀態保留的條目。最後喚醒所有等待wait狀態的Thread。這裏要更新cache緩存中key狀態的原因是:當Thread1在notifyAll Thread線程後,在第一個同步代碼塊中的Thread2會被喚醒,此時Thread2執行continue語句進入下一輪循環,執行cache.get(key)時發現key對應的弱引用已經生成了,此時Thread2可以跳出循環返回key對應的弱引用(即key對應的代理類),而如果Thread1在生成代理類的弱引用沒有更改key對應的狀態的話,在Thread1喚醒所有線程後,Thread2在執行cache.get(key)時判斷key狀態,仍會進入②條件分支中,這樣會導致Thread在第一個同步代碼塊永遠出不來。
Map cache 用於存儲以‘接口列表名稱’爲key,以‘當前key對應的代理類生成的狀態’爲value的集合,key對應的代理類生成的狀態(即value的值主要包含兩種狀態:a.pendingGenerationMarkerr(當前正在生成特定的代理類)&&b.已經完成key對應的代理類生成(即已獲取代理類的弱引用))
代碼剖析總結3:
在執行步驟1.1.6中完成了遍歷接口名稱數組,對數組元素分別調用Class.forName(String name, boolean initialize,ClassLoader loader) 返回指定name字符串名稱的類或接口相關聯的Class對象 :使用給定的類加載器返回與具有給定字符串名稱的類或接口相關聯的Class對象。在步驟 1.1.9中執完成序列化的接口名稱列表,用於使用代理接口的字符串表示作爲代理類緩存中的鍵。在步驟1.1.12.2中執行new ProxyGenerator(name, interfaces).generateClassFile();調用以指定名稱和給定接口名稱列表爲參數的構造函數創建用於生成動態代理類的ProxyGenerator類對象,並調用generateClassFile()方法爲代理類生成類文件返回 byte[] classFile 數據。在步驟1.1.12.2.1中,循環遍歷目標類實現的接口名稱對應的Class對象,調用Class.getMethods()方法返回該接口的Class對象的所有Method對象數組。並調用ProxyGenerator類的addProxyMethod(Method m, Class fromClass)方法,實現添加一個要被代理的方法,通過創建一個新的ProxyMethod對象或者爲一個重複的方法增加一箇舊的方法。目標是爲代理類添加每一個目標類實現的接口對象中的方法。所以代理類是和目標類具有相同接口的類,我們可以採用調用原類相同的方式來調用代理類。在步驟1.1.12.2.3.1中調用ProxyMethod類的generateMethod()方法,用於生成代理方法,該方法參數中包含InvocationHandler,且方法體調用InvocationHandler的invoke()方法,來實現“在使用代理類調用目標類的接口方法時,通過代理類中的InvocationHandler對象回調InvocationHandler的invoke()方法”。
主要查找的源碼文件有:Proxy.java、 ProxyGenerator.java
-------------------------------------------------------------------------------------------------------
補充:
通過調用ProxyGenerator的generateProxyClass 方法產生binary data,然後寫入文件,最後通過反編譯工具來查看內部實現原理。 反編譯後的 ProxySubject.java Proxy 靜態方法 newProxyInstance
import java.lang.reflect.*; public final class ProxySubject extends Proxy implements Subject { public ProxySubject(InvocationHandler invocationhandler){ super(invocationhandler); } }
可參考:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章