JAVA 動態代理原理和實現


在 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM