日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java动态代理原理及解析

發布時間:2024/2/28 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java动态代理原理及解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自?http://blog.csdn.net/scplove/article/details/52451899

代理:設計模式

代理模式是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個真實對象的訪問。代理類負責為委托類預處理消息,過濾消息并轉發消息,以及進行消息被委托類執行后的后續處理。

通過代理層這一中間層,有效的控制對于真實委托類對象的直接訪問,同時可以實現自定義的控制策略(Spring的AOP機制),設計上獲得更大的靈活性。

java動態代理的類和接口(jdk1.6源碼)

1,java.lang.reflect.Proxy:動態代理機制的主類,提供一組靜態方法為一組接口動態的生成對象和代理類。

// 方法 1: 該方法用于獲取指定代理對象所關聯的調用處理器 public static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:該方法用于獲取關聯于指定類裝載器和一組接口的動態代理類的類對象 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)// 方法 3:該方法用于判斷指定類對象是否是一個動態代理類 public static boolean isProxyClass(Class<?> cl) // 方法 4:該方法用于為指定類裝載器、一組接口及調用處理器生成動態代理類實例 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2,java.lang.reflect.InvocationHandler:調用處理器接口,自定義invokle方法,用于實現對于真正委托類的代理訪問。

/**該方法負責集中處理動態代理類上的所有方法調用。第一個參數既是代理類實例,第二個參數是被調用的方法對象第三個方法是調用參數。調用處理器根據這三個參數進行預處理或分派到委托類實例上發射執行 */ public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3,java.lang.ClassLoader:類裝載器類,將類的字節碼裝載到 Java 虛擬機(JVM)中并為其定義類對象,然后該類才能被使用。Proxy類與普通類的唯一區別就是其字節碼是由 JVM 在運行時動態生成的而非預存在于任何一個 .class 文件中。?
每次生成動態代理類對象時都需要指定一個類裝載器對象:newProxyInstance()方法第一個參數

動態代理機制

java動態代理創建對象的過程為如下步驟:?
1,通過實現 InvocationHandler 接口創建自己的調用處理器;

// InvocationHandlerImpl 實現了 InvocationHandler 接口,并能實現方法調用從代理類到委托類的分派轉發 // 其內部通常包含指向委托類實例的引用,用于真正執行分派轉發過來的方法調用 InvocationHandler handler = new InvocationHandlerImpl(..);
  • 1
  • 2
  • 3

2,通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創建動態代理類;

// 通過 Proxy 為包括 Interface 接口在內的一組接口動態創建代理類的類對象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
  • 1
  • 2

3,通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;

// 通過反射從生成的類對象獲得構造函數對象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
  • 1
  • 2

4,通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入。

// 通過構造函數對象創建動態代理類實例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
  • 1
  • 2

為了簡化對象創建過程,Proxy類中的newProxyInstance方法封裝了2~4,只需兩步即可完成代理對象的創建。

// InvocationHandlerImpl 實現了 InvocationHandler 接口,并能實現方法調用從代理類到委托類的分派轉發 InvocationHandler handler = new InvocationHandlerImpl(..); // 通過 Proxy 直接創建動態代理類實例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

動態代理的注意點:?
1,包:代理接口是public,則代理類被定義在頂層包(package為空),否則(default),代理類被定義在該接口所在包,

2,生成的代理類為public final,不能被繼承,

3,類名:格式是“$ProxyN”,N是逐一遞增的數字,代表Proxy被第N次動態生成的代理類,要注意,對于同一組接口(接口的排列順序也相同),不會重復創建動態代理類,而是返回一個先前已經創建并緩存了的代理類對象。提高了效率。

4,類繼承關系:

?
Proxy 類是它的父類,這個規則適用于所有由 Proxy 創建的動態代理類。(也算是java動態代理的一處缺陷,java不支持多繼承,所以無法實現對class的動態代理,只能對于Interface的代理)而且該類還實現了其所代理的一組接口,這就是為什么它能夠被安全地類型轉換到其所代理的某接口的根本原因。

5,代理類的根類 java.lang.Object 中有三個方法也同樣會被分派到調用處理器的 invoke 方法執行,它們是 hashCode,equals 和 toString,?
代碼在反編譯中?

一個動態代理的demo

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class HelloServiceProxy 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);} @Override /** * 同過代理對象調用方法首先進入這個方法. * @param proxy --代理對象 * @param method -- 方法,被調用方法. * @param args -- 方法的參數 */ public Object invoke(Object proxy , Method method, Object[] args) throws Throwable { System.err.println("############我是JDK動態代理################"); Object result = null; //反射方法前調用 System.err.println("我準備說hello。"); //反射執行方法 相當于調用target.sayHelllo; result=method.invoke(target, args); //反射方法后調用. System.err.println("我說過hello了"); return result; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

其中,bind方法中的newProxyInstanc方法,就是生成一個代理對象,第一個參數是類加載器,第二個參數是真實委托對象所實現的的接口(代理對象掛在那個接口下),第三個參數this代表當前HelloServiceProxy類,換句話說是使用HelloServiceProxy作為對象的代理。

invoke方法有三個參數:第一個proxy是代理對象,第二個是當前調用那個方法,第三個是方法的參數。

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經過綁定,就會進入invoke方法里面了。 service.sayHello("張三"); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

測試結果:

############我是JDK動態代理################ 我準備說hello。 hello 張三 我說過hello了
  • 1
  • 2
  • 3
  • 4

源碼跟蹤

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Proxy 靜態方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 檢查 h 不為空,否則拋異常if (h == null) { throw new NullPointerException(); } // 獲得與制定類裝載器和一組接口相關的代理類類型對象/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, interfaces); // 通過反射獲取構造函數對象并生成代理類實例/** Invoke its constructor with the designated invocation handler.*/try {final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;SecurityManager sm = System.getSecurityManager();if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {// create proxy instance with doPrivilege as the proxy class may// implement non-public interfaces that requires a special permissionreturn 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 (InstantiationException e) {throw new InternalError(e.toString());} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString());}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

動態代理真正的關鍵是在?getProxyClass0?方法,

getProxyClass0方法分析

方法分為四個步驟:?
1,對這組接口進行一定程度的安全檢查?
檢查接口類對象是否對類裝載器可見并且與類裝載器所能識別的接口類對象是完全相同的,還會檢查確保是 interface 類型而不是 class 類型。

2,從 loaderToCache 映射表中獲取以類裝載器對象為關鍵字所對應的緩存表,如果不存在就創建一個新的緩存表并更新到 loaderToCache。?
loaderToCache存放鍵值對(接口名字列表,動態生成的代理類的類對象引用)。當代理類正在被創建時它會臨時保存(接口名字列表,pendingGenerationMarker)。標記 pendingGenerationMarke 的作用是通知后續的同類請求(接口數組相同且組內接口排列順序也相同)代理類正在被創建,請保持等待直至創建完成。

/** 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);}} 。。。。。 do { // 以接口名字列表作為關鍵字獲得對應 cache 值Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 如果已經創建,直接返回return proxyClass; } else if (value == pendingGenerationMarker) { // 代理類正在被創建,保持等待try { cache.wait(); } catch (InterruptedException e) { } // 等待被喚醒,繼續循環并通過二次檢查以確保創建完成,否則重新等待continue; } else { // 標記代理類正在被創建cache.put(key, pendingGenerationMarker); // break 跳出循環已進入創建過程break; } while (true);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3,動態創建代理類的class對象

/*** Choose a name for the proxy class to generate.*/long num;synchronized (nextUniqueNumberLock) {num = nextUniqueNumber++;}String proxyName = proxyPkg + proxyClassNamePrefix + num;/** Verify that the class loader hasn't already* defined a class with the chosen name.*/// 動態地生成代理類的字節碼數組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());}// 把生成的代理類的類對象記錄進 proxyClasses 表proxyClasses.put(proxyClass, null);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

首先根據規則(接口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(dotToSlash(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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

4,代碼生成過程進入結尾部分,根據結果更新緩存表,如果成功則將代理類的類對象引用更新進緩存表,否則清楚緩存表中對應關鍵值,最后喚醒所有可能的正在等待的線程。

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) {cache.put(key, new WeakReference(proxyClass));} else {cache.remove(key);}cache.notifyAll();}} return proxyClass;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

InvocationHandler解析

代碼參考:http://rejoy.iteye.com/blog/1627405?
通過getProxyClass0方法中生成具體的class文件的過程,定義path,講class文件寫到指定的磁盤中,反編譯生成的代理class文件。?
發現在靜態代碼塊中獲取了的方法有:Object中的equals方法、Object中的hashCode方法、Object中toString方法 ,?以及invoke的接口方法。

后語

至此,JDK是動態生成代理類,并通過調用解析器,執行接口實現的方法的原理已經一目了然。動態代理加上反射,是很多框架的基礎。比如Spring的AOP機制,自定義前置后置通知等控制策略,以及mybatis中的運用反射和動態代理來實現插件技術等等。

文章參考:?
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html?
http://blog.csdn.net/ykzhen2015/article/details/50312651


總結

以上是生活随笔為你收集整理的java动态代理原理及解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。