1. 程式人生 > >JAVA 動態代理原理和實現

JAVA 動態代理原理和實現

ror binary lose ole jdk 動態代理 參數 try lob rac

在 Java 中動態代理和代理都很常見,幾乎是所有主流框架都用到過的知識。在面試中也是經常被提到的話題,於是便總結了本文。

Java動態代理的基本原理為:被代理對象需要實現某個接口(這是前提),代理對象會攔截對被代理對象的方法調用,在其中可以全然拋棄被代理對象的方法實現而完成另外的功能,也可以在被代理對象方法調用的前後增加一些額外的功能。

動態代理可以為其他對象提供一個代理以控制對某個對象的訪問。

代理類負責為委托類預處理消息,過濾消息並轉發消息,以及進行消息被委托類執行後的後續處理。

JDK 動態代理

動態代理的核心其實就是代理對象的生成,即 Proxy.newProxyInstance(classLoader, proxyInterface, handler)。

讓我們進入newProxyInstance方法觀摩下,核心代碼其實就三行。

這個方法需要三個參數:

  • ClassLoader,用於加載代理類的 Loader 類,通常這個 Loader 和被代理的類是同一個 Loader 類。
  • Interfaces,是要被代理的那些那些接口。
  • InvocationHandler,就是用於執行除了被代理接口中方法之外的用戶自定義的操作,他也是用戶需要代理的最終目的。用戶調用目標方法都被代理到 InvocationHandler 類中定義的唯一方法 invoke 中。
1 2 3 4 5 6 //獲取代理類 Class cl = getProxyClass(loader, interfaces);
//獲取帶有InvocationHandler參數的構造方法 Constructor cons = cl.getConstructor(constructorParams); //把handler傳入構造方法生成實例 return (Object) cons.newInstance(new Object[] { h });

一個典型的動態代理創建對象過程可分為以下四個步驟:

1、通過實現InvocationHandler接口創建調用處理器

1 IvocationHandler handler = new InvocationHandlerImpl(...);

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

1 Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

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

1 Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

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

1 Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

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

1 2 Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new InvocationHandlerImpl (real));

生成的proxySubject繼承Proxy類實現Subject接口。實現的Subject的方法實際是調用處理器的invoke方法,而invoke方法利用反射調用的是被代理對象的方法(Object result=method.invoke(proxied,args));
重點Proxy.newProxyInstance,源碼分析,會在其他文檔中單獨總結記錄。類Proxy的getProxyClass方法調用ProxyGenerator的 generateProxyClass方法產生ProxySubject.class的二進制數據。

創建代理對象時序圖

技術分享圖片

獲取代理類

getProxyClass(loader, interfaces)方法用於獲取代理類,它主要做了三件事情:

在當前類加載器的緩存裏搜索是否有代理類,沒有則生成代理類並緩存在本地JVM裏。

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 // 緩存的key使用接口名稱生成的List Object key = Arrays.asList(interfaceNames); synchronized (cache) { do { Object value = cache.get(key); // 緩存裏保存了代理類的引用 if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 代理類已經存在則返回 return proxyClass; } else if (value == pendingGenerationMarker) { // 如果代理類正在產生,則等待 try { cache.wait(); } catch (InterruptedException e) { } continue; } else { //沒有代理類,則標記代理準備生成 cache.put(key, pendingGenerationMarker); break; } } while (true); }

生成並加載代理類

代理類的生成主要是以下這兩行代碼:

1 2 3 4 //生成代理類的字節碼文件並保存到硬盤中(默認不保存到硬盤) proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); //使用類加載器將字節碼加載到內存中 proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

代理類的生成過程

ProxyGenerator.generateProxyClass()方法屬於sun.misc包下,Oracle並沒有提供源代碼,但是我們可以使用
JD-GUI這樣的反編譯軟件打開jre\lib\rt.jar來一探究竟,以下是其核心代碼的分析。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //添加接口中定義的方法,此時方法體為空 for (int i = 0; i < this.interfaces.length; i++) { localObject1 = this.interfaces[i].getMethods(); for (int k = 0; k < localObject1.length; k++) { addProxyMethod(localObject1[k], this.interfaces[i]); } } //添加一個帶有InvocationHandler的構造方法 MethodInfo localMethodInfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1); //循環生成方法體代碼(省略) //方法體裏生成調用InvocationHandler的invoke方法代碼。(此處有所省略) this.cp.getInterfaceMethodRef("InvocationHandler", "invoke", "Object; Method; Object;") //將生成的字節碼,寫入硬盤,前面有個if判斷,默認情況下不保存到硬盤。 localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class"); localFileOutputStream.write(this.val$classFile);

生成的代理類源碼

那麽通過以上分析,我們可以推出動態代理為我們生成了一個這樣的代理類。把方法doSomeThing的方法體修改為調用LogInvocationHandler的invoke方法。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ProxyBusiness extends Proxy implements IBusiness, IBusiness2 { private LogInvocationHandler h; @Override public void doSomeThing2() { try { Method m = (h.target).getClass().getMethod("doSomeThing2",null); h.invoke(this, m, null); } catch (Throwable e) { // 異常處理(略) } } @Override public boolean doSomeThing() { try { Method m = (h.target).getClass().getMethod("doSomeThing", null); return (Boolean) h.invoke(this, m, null); } catch (Throwable e) { // 異常處理(略) } return false; } public ProxyBusiness(LogInvocationHandler h) { this.h = h; }

測試代理的代碼如下:

1 2 3 4 5 6 7 //測試 public static void main(String[] args) { //構建AOP的Advice LogInvocationHandler handler = new LogInvocationHandler(new Business()); new ProxyBusiness(handler).doSomeThing(); new ProxyBusiness(handler).doSomeThing2(); }

下面看一個自定義代理的實現。

被代理類接口

1 2 3 public interface Subject { public void doSomething(); }

被代理類

1 2 3 4 5 6 //目標對象 public class RealSubject implements Subject{ public void doSomething() { System.out.println( "call doSomething()" ); } }

調用處理器(切面)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ProxyHandler implements InvocationHandler { private Object proxied; public ProxyHandler( Object proxied ) { this.proxied = proxied; } public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { //在轉調具體目標對象之前,可以執行一些功能處理 System.out.println( "doSomething before" ); //轉調具體目標對象的方法 return method.invoke( proxied, args); //在轉調具體目標對象之後,可以執行一些功能處理 System.out.println( "doSomething after" ); } }

測試我們的代理實現

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DynamicProxy { public static void main( String args[] ) { RealSubject real = new RealSubject(); Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new ProxyHandler(real)); proxySubject.doSomething(); //write proxySubject class binary data to file createProxyClassFile(); } public static void createProxyClassFile() { String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } ); try{ FileOutputStream out = new FileOutputStream( name + ".class" ); out.write( data ); out.close(); }catch( Exception e ) { e.printStackTrace(); } } }

運行結果:

1 2 3 doSomething before call doSomething() doSomething after

Proxy 接口

Proxy 的主要靜態變量

1 2 3 4 5 6 7 8 9 10 11 12 13 // 映射表:用於維護類裝載器對象到其對應的代理類緩存 private static Map loaderToCache = new WeakHashMap(); // 標記:用於標記一個動態代理類正在被創建中 private static Object pendingGenerationMarker = new Object(); // 同步表:記錄已經被創建的動態代理類類型,主要被方法 isProxyClass 進行相關的判斷 private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 關聯的調用處理器引用 protected InvocationHandler h; Proxy的構造方法 // 由於 Proxy 內部從不直接調用構造函數,所以 private 類型意味著禁止任何調用 private Proxy() {} // 由於 Proxy 內部從不直接調用構造函數,所以 protected 意味著只有子類可以調用 protected Proxy(InvocationHandler h) {this.h = h;}

ProxySubject 源碼

創建的代理類 ProxySubject.class

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 57 58 59 60 61 import java.lang.reflect.*; public final class ProxySubject extends Proxy implements Subject{ private static Method m1; private static Method m0; private static Method m3; private static Method m2; public ProxySubject(InvocationHandler invocationhandler){ super(invocationhandler); } public final boolean equals(Object obj){ try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); }catch(Error _ex) { }catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); }catch(Error _ex) { }catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } } /*關鍵部分*/ public final void doSomething() { try { // Proxy類中protected InvocationHandler h;關聯的調用處理器引用 super.h.invoke(this, m3, null); return; }catch(Error _ex) { }catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } } static{ try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); }catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); }catch(ClassNotFoundException classnotfoundexception){ throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }

CGLib 動態代理

動態字節碼生成。使用動態字節碼生成技術實現AOP原理是在運行期間目標字節碼加載後,生成目標類的子類,將切面邏輯加入到子類中,所以使用Cglib實現AOP不需要基於接口。

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 public static void main(String[] args) { byteCodeGe(); } public static void byteCodeGe() { //創建一個織入器 Enhancer enhancer = new Enhancer(); //設置父類 enhancer.setSuperclass(Business.class); //設置需要織入的邏輯 enhancer.setCallback(new LogIntercept()); //使用織入器創建子類 IBusiness2 newBusiness = (IBusiness2) enhancer.create(); newBusiness.doSomeThing2(); } /** * 記錄日誌 */ public static class LogIntercept implements MethodInterceptor { @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable { //執行原有邏輯,註意這裏是invokeSuper Object rev = proxy.invokeSuper(target, args); //執行織入的日誌 if (method.getName().equals("doSomeThing2")) { System.out.println("記錄日誌"); } return rev; } }

轉自:https://www.xttblog.com/?p=2732

JAVA 動態代理原理和實現