1. 程式人生 > >轉:jdk動態代理實現

轉:jdk動態代理實現

原文連結: jdk動態代理

注:文章中用常用的流程實現 動態代理,流程邏輯比較清晰。文章後面對 “為什麼要使用介面” 原理分析還未細看。

jdk的動態代理為什麼用介面,內部是什麼原理呢?看了幾篇文章貌似都沒講的清楚明白,因此來解釋一下。

先通過一個簡單例子實現功能:

 1 //介面
 2 public interface SayService {
 3  
 4     void say(String name);
 5 }
 6 //實現類
 7 public class SayServiceImpl implements SayService{
 8  
 9     @Override
10 public void say(String name) { 11 System.out.println(name); 12 } 13 }

然後再自定義一個增強類, 實現InvocationHandler介面:

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3  
 4 public class WavingInvocationHandler  implements InvocationHandler{
 5  
 6     private
Object target; 7 8 public void setTarget(Object target) { 9 this.target = target; 10 } 11 12 @Override 13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 14 System.out.println("方法執行之前!"); 15 Object obj = method.invoke(target, args);
16 System.out.println("方法執行之後!"); 17 return obj; 18 } 19 }

編寫測試方法:

 1 import sun.misc.ProxyGenerator;
 2  
 3 import java.io.File;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 import java.lang.reflect.Proxy;
 8  
 9 public class Test {
10  
11     public static void main(String[] args) {
12  
13         //介面
14         SayService sayService = new SayServiceImpl();
15         //織入類
16         WavingInvocationHandler handler = new WavingInvocationHandler();
17         handler.setTarget(sayService);
18         //代理類--增強的物件
19         SayService s = (SayService) Proxy.newProxyInstance(
20                 sayService.getClass().getClassLoader(),
21                 sayService.getClass().getInterfaces(), handler);
22  
23         s.say("say()");//執行代理物件完成業務
24         /**
25          方法執行之前!
26          say()
27          方法執行之後!
28          */
29  
30         //將jdk中生成代理類輸出到本地.Class檔案,之後可以通過反編譯軟體開啟檢視
31         createProxyClassFile("test12345",sayService.getClass().getInterfaces());
32  
33     }
34  
35     private static void createProxyClassFile(String name,Class<?> [] interfaces){
36         byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//該方法為jdk中生成代理類的核心方法
37         FileOutputStream out =null;
38         try {
39             out = new FileOutputStream(name+".class");
40             System.out.println((new File(name)).getAbsolutePath());
41             out.write(data);
42         } catch (FileNotFoundException e) {
43             e.printStackTrace();
44         } catch (IOException e) {
45             e.printStackTrace();
46         }finally {
47             if(null!=out) try {
48                 out.close();
49             } catch (IOException e) {
50                 e.printStackTrace();
51             }
52         }
53     }
54     
55 }

好奇心強的小夥伴一定看過newProxyInstance方法看過了,裡面的getProxyClass方法建立代理類:

1 /*
2  * Look up or generate the designated proxy class.
3  */
4 Class<?> cl = getProxyClass0(loader, intfs);

具體是裡面的哪個方法生成的,小夥伴們不用找半天了,慢慢debug後會發現,就是上面提到的

1 ProxyGenerator.generateProxyClass()

通過該方法生成的代理類如下:通過反編譯檢視原始碼,一看便知介面的作用

JDK的動態代理是靠多型和反射來實現的,它生成的代理類需要實現你傳入的介面,並通過反射來得到介面的方法物件(下文中的m3),並將此方法物件傳參給增強類(上文中的WavingInvocationHandler類)的invoke方法去執行,從而實現了代理功能,故介面是jdk動態代理的核心實現方式,沒有它就無法通過反射找到方法,所以這也是必須有介面的原因。不知道大家明白否

 1 public final class test12345 extends Proxy implements SayService {
 2     //1、該類實現你傳入的介面並實現方法 
 3     // 2、通過構造方法傳入你定義的增強類物件 
 4     // 3、通過反射該介面,得到接口裡的Method物件並傳參給增強類,然後執行Invoke實現功能
 5     private static Method m1;
 6     private static Method m2;
 7     private static Method m3;
 8     private static Method m0;
 9  
10     public test12345(InvocationHandler paramInvocationHandler) {
11         super(paramInvocationHandler);//2.此處的super指向Proxy中的構造方法並賦值(下文的h就是此處的增強類物件),
12     }
13  
14     //略去無關的hashcode和equals方法
15     public final void say(String paramString) {
16         try {
17             this.h.invoke(this, m3, new Object[]{paramString});//3.此處的h就是InvocationHandler物件,invoke就是你增強類裡的方法,傳入介面的方法和引數並執行你增強類裡的invoke方法,即完成了整個操作!
18         } catch (Error | RuntimeException localError) {
19             throw localError;
20         } catch (Throwable localThrowable) {
21             throw new UndeclaredThrowableException(localThrowable);
22         }
23     }
24  
25     static {//1.靜態程式碼塊給屬性賦值,初始化介面中的方法物件(主要是下面的m3)
26         try {
27             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
28             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
29             m3 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")});
30             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
31         } catch (NoSuchMethodException localNoSuchMethodException) {
32             throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
33         } catch (ClassNotFoundException localClassNotFoundException) {
34             throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
35         }
36     }
37 }

 


---------------------
作者:銳銳
來源:CSDN
原文:https://blog.csdn.net/ray890206/article/details/70146029
版權宣告:本文為博主原創文章,轉載請附上博文連結!