Rumah Java javaTutorial Java中动态代理的实现教程

Java中动态代理的实现教程

Jun 30, 2017 am 09:52 AM
invoke soalan

以下的内容部分参考了网络上的内容,在此对原作者表示感谢! 

        Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。 
        首先,invoke方法的完整形式如下: 

Java代码  收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  

  2.     {  

  3.         method.invoke(obj, args);  

  4.         return null;  

  5.     }  


        首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下: 

Java代码  收藏代码
  1. A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.  


        由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。 

        为了方便说明,这里写一个简单的例子来实现动态代理。 

       

Java代码  收藏代码
  1. //抽象角色(动态代理只能代理接口)  

  2. public interface Subject {  

  3.     public void request();  

  4. }  


Java代码  收藏代码
  1. //真实角色:实现了Subject的request()方法  

  2. public class RealSubject implements Subject{  

  3.     public void request(){  

  4.         System.out.println("From real subject.");  

  5.     }  

  6. }  


Java代码  收藏代码
  1. //实现了InvocationHandler  

  2. public class DynamicSubject implements InvocationHandler  

  3. {  

  4.     private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  

  5.     public DynamicSubject()  

  6.     {  

  7.     }  

  8.     public DynamicSubject(Object obj)  

  9.     {  

  10.         this.obj = obj;  

  11.     }  

  12.     //这个方法不是我们显示的去调用  

  13.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  

  14.     {  

  15.         System.out.println("before calling " + method);  

  16.         method.invoke(obj, args);  //----->这一步详看下一篇文章,java 通过反射机制调用某个类的方法,看完你会了解的。

  17.         System.out.println("after calling " + method);  

  18.         return null;  

  19.     }  

  20. }  


Java代码  收藏代码
  1. //客户端:生成代理实例,并调用了request()方法  

  2. public class Client {  

  3.     public static void main(String[] args) throws Throwable{  

  4.         // TODO Auto-generated method stub  

  5.         Subject rs=new RealSubject();//这里指定被代理类  

  6.         InvocationHandler ds=new DynamicSubject(rs);  

  7.         Class cls=rs.getClass();  

  8.         //以下是一次性生成代理  

  9.         Subject subject=(Subject) Proxy.newProxyInstance(  

  10.                 cls.getClassLoader(),cls.getInterfaces(), ds);  

  11.         //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  

  12.         System.out.println(subject instanceof Proxy);  

  13.         //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  

  14.         System.out.println("subject的Class类是:"+subject.getClass().toString());  

  15.         System.out.print("subject中的属性有:");  

  16.         Field[] field=subject.getClass().getDeclaredFields();  

  17.         for(Field f:field){  

  18.             System.out.print(f.getName()+", ");  

  19.         }  

  20.         System.out.print("\n"+"subject中的方法有:");  

  21.         Method[] method=subject.getClass().getDeclaredMethods();  

  22.         for(Method m:method){  

  23.             System.out.print(m.getName()+", ");  

  24.         }  

  25.         System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());  

  26.         System.out.print("\n"+"subject实现的接口是:");  

  27.         Class[] interfaces=subject.getClass().getInterfaces();  

  28.         for(Class i:interfaces){  

  29.             System.out.print(i.getName()+", ");  

  30.         }  

  31.         System.out.println("\n\n"+"运行结果为:");  

  32.         subject.request();  

  33.     }  

  34. }  


Xml代码  收藏代码
  1. 运行结果如下:此处省略了包名,***代替  

  2. true  

  3. subject的Class类是:class $Proxy0  

  4. subject中的属性有:m1, m3, m0, m2,   

  5. subject中的方法有:request, hashCode, equals, toString,   

  6. subject的父类是:class java.lang.reflect.Proxy  

  7. subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,   

  8. 运行结果为:  

  9. before calling public abstract void ***.Subject.request()  

  10. From real subject.  

  11. after calling public abstract void ***.Subject.request()  



PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class $Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。 

        从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下: 

        从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码: 

Java代码  收藏代码
  1. public static Object newProxyInstance(ClassLoader loader,  

  2.         Class[] interfaces,  

  3.         InvocationHandler h)  

  4. throws IllegalArgumentException  

  5. {  

  6.     if (h == null) {  

  7.         throw new NullPointerException();  

  8.     }  

  9.     /* 

  10.      * Look up or generate the designated proxy class. 

  11.      */  

  12.     Class cl = getProxyClass(loader, interfaces);  

  13.     /* 

  14.      * Invoke its constructor with the designated invocation handler. 

  15.      */  

  16.     try {  

  17.            /* 

  18.             * Proxy源码开始有这样的定义: 

  19.             * private final static Class[] constructorParams = { InvocationHandler.class }; 

  20.             * cons即是形参为InvocationHandler类型的构造方法 

  21.            */  

  22.         Constructor cons = cl.getConstructor(constructorParams);  

  23.         return (Object) cons.newInstance(new Object[] { h });  

  24.     } catch (NoSuchMethodException e) {  

  25.         throw new InternalError(e.toString());  

  26.     } catch (IllegalAccessException e) {  

  27.         throw new InternalError(e.toString());  

  28.     } catch (InstantiationException e) {  

  29.         throw new InternalError(e.toString());  

  30.     } catch (InvocationTargetException e) {  

  31.         throw new InternalError(e.toString());  

  32.     }  

  33. }  



        Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)做了以下几件事.
        (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类. 
        (2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下: 

Java代码  收藏代码
  1. class Proxy{  

  2.     InvocationHandler h=null;  

  3.     protected Proxy(InvocationHandler h) {  

  4.         this.h = h;  

  5.     }  

  6.     ...  

  7. }  



        来看一下这个继承了Proxy的$Proxy0的源代码: 

Java代码  收藏代码
  1. public final class $Proxy0 extends Proxy implements Subject {  

  2.     private static Method m1;  

  3.     private static Method m0;  

  4.     private static Method m3;  

  5.     private static Method m2;  

  6.     static {  

  7.         try {  

  8.             m1 = Class.forName("java.lang.Object").getMethod("equals",  

  9.                     new Class[] { Class.forName("java.lang.Object") });  

  10.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  

  11.                     new Class[0]);  

  12.             m3 = Class.forName("***.RealSubject").getMethod("request",  

  13.                     new Class[0]);  

  14.             m2 = Class.forName("java.lang.Object").getMethod("toString",  

  15.                     new Class[0]);  

  16.         } catch (NoSuchMethodException nosuchmethodexception) {  

  17.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  

  18.         } catch (ClassNotFoundException classnotfoundexception) {  

  19.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  

  20.         }  

  21.     } //static  

  22.     public $Proxy0(InvocationHandler invocationhandler) {  

  23.         super(invocationhandler);  

  24.     }  

  25.     @Override  

  26.     public final boolean equals(Object obj) {  

  27.         try {  

  28.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  

  29.         } catch (Throwable throwable) {  

  30.             throw new UndeclaredThrowableException(throwable);  

  31.         }  

  32.     }  

  33.     @Override  

  34.     public final int hashCode() {  

  35.         try {  

  36.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  

  37.         } catch (Throwable throwable) {  

  38.             throw new UndeclaredThrowableException(throwable);  

  39.         }  

  40.     }  

  41.     public final void request() {  

  42.         try {  

  43.             super.h.invoke(this, m3, null);  

  44.             return;  

  45.         } catch (Error e) {  

  46.         } catch (Throwable throwable) {  

  47.             throw new UndeclaredThrowableException(throwable);  

  48.         }  

  49.     }  

  50.     @Override  

  51.     public final String toString() {  

  52.         try {  

  53.             return (String) super.h.invoke(this, m2, null);  

  54.         } catch (Throwable throwable) {  

  55.             throw new UndeclaredThrowableException(throwable);  

  56.         }  

  57.     }  

  58. }  



        接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。 

PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看一下getProxyClass的源码,很长=。= 
        2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。 

Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的? 
A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。

Atas ialah kandungan terperinci Java中动态代理的实现教程. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Selesaikan masalah 'ralat: definisi semula kelas 'Nama Kelas'' yang muncul dalam kod C++ Selesaikan masalah 'ralat: definisi semula kelas 'Nama Kelas'' yang muncul dalam kod C++ Aug 25, 2023 pm 06:01 PM

Selesaikan masalah "error:redefinitionofclass'ClassName'" dalam kod C++ Dalam pengaturcaraan C++, kita sering menghadapi pelbagai ralat kompilasi. Salah satu ralat biasa ialah "error:redefinitionofclass 'ClassName'" (ralat definisi semula kelas 'ClassName'). Ralat ini biasanya berlaku apabila kelas yang sama ditakrifkan beberapa kali. Artikel ini akan

Masalah penilaian kesan pengelompokan dalam algoritma pengelompokan Masalah penilaian kesan pengelompokan dalam algoritma pengelompokan Oct 10, 2023 pm 01:12 PM

Masalah penilaian kesan pengelompokan dalam algoritma pengelompokan memerlukan contoh kod khusus Pengelompokan ialah kaedah pembelajaran tanpa pengawasan yang mengelompokkan sampel yang serupa ke dalam satu kategori dengan mengelompokkan data. Dalam algoritma pengelompokan, cara menilai kesan pengelompokan adalah isu penting. Artikel ini akan memperkenalkan beberapa penunjuk penilaian kesan pengelompokan yang biasa digunakan dan memberikan contoh kod yang sepadan. 1. Indeks penilaian kesan pengelompokan Pekali Siluet Pekali siluet menilai kesan pengelompokan dengan mengira kehampiran sampel dan tahap pemisahan daripada kelompok lain.

Apakah yang perlu saya lakukan jika saya tidak boleh memuat turun Steam pada Windows 10? Apakah yang perlu saya lakukan jika saya tidak boleh memuat turun Steam pada Windows 10? Jul 07, 2023 pm 01:37 PM

Steam ialah platform permainan yang sangat popular dengan banyak permainan berkualiti tinggi, tetapi sesetengah pengguna win10 melaporkan bahawa mereka tidak boleh memuat turun wap Apa yang sedang berlaku? Kemungkinan besar alamat pelayan IPv4 pengguna tidak ditetapkan dengan betul. Untuk menyelesaikan masalah ini, anda boleh cuba memasang Steam dalam mod keserasian, dan kemudian mengubah suai pelayan DNS secara manual kepada 114.114.114.114, dan anda sepatutnya boleh memuat turunnya kemudian. Apa yang perlu dilakukan jika Win10 tidak dapat memuat turun Steam: Di bawah Win10, anda boleh cuba memasangnya dalam mod keserasian Selepas mengemas kini, anda mesti mematikan mod keserasian, jika tidak, halaman web tidak akan dimuatkan. Klik sifat pemasangan program untuk menjalankan program dalam mod keserasian. Mulakan semula untuk meningkatkan memori, kuasa

Ajar anda cara mendiagnosis masalah iPhone biasa Ajar anda cara mendiagnosis masalah iPhone biasa Dec 03, 2023 am 08:15 AM

Dikenali dengan prestasi yang berkuasa dan ciri serba boleh, iPhone tidak terlepas daripada cegukan atau kesukaran teknikal sekali-sekala, ciri biasa di kalangan peranti elektronik yang kompleks. Mengalami masalah iPhone boleh mengecewakan, tetapi biasanya penggera tidak diperlukan. Dalam panduan komprehensif ini, kami menyasarkan untuk menyahmistifikasi beberapa cabaran yang paling biasa dihadapi yang berkaitan dengan penggunaan iPhone. Pendekatan langkah demi langkah kami direka untuk membantu anda menyelesaikan isu lazim ini, menyediakan penyelesaian praktikal dan petua penyelesaian masalah untuk mengembalikan peralatan anda dalam keadaan berfungsi terbaik. Sama ada anda menghadapi masalah atau isu yang lebih kompleks, artikel ini boleh membantu anda menyelesaikannya dengan berkesan. Petua Penyelesaian Masalah Umum Sebelum menyelidiki langkah penyelesaian masalah khusus, berikut adalah beberapa yang berguna

Bagaimana untuk menyelesaikan masalah yang jQuery tidak dapat memperoleh nilai elemen bentuk Bagaimana untuk menyelesaikan masalah yang jQuery tidak dapat memperoleh nilai elemen bentuk Feb 19, 2024 pm 02:01 PM

Untuk menyelesaikan masalah yang jQuery.val() tidak boleh digunakan, contoh kod khusus diperlukan Untuk pembangun bahagian hadapan, menggunakan jQuery ialah salah satu operasi biasa. Antaranya, menggunakan kaedah .val() untuk mendapatkan atau menetapkan nilai elemen borang adalah operasi yang sangat biasa. Walau bagaimanapun, dalam beberapa kes tertentu, masalah tidak dapat menggunakan kaedah .val() mungkin timbul. Artikel ini akan memperkenalkan beberapa situasi dan penyelesaian biasa, serta memberikan contoh kod khusus. Penerangan Masalah Apabila menggunakan jQuery untuk membangunkan halaman hadapan, kadangkala anda akan menghadapi

Selesaikan ralat PHP: masalah yang dihadapi semasa mewarisi kelas induk Selesaikan ralat PHP: masalah yang dihadapi semasa mewarisi kelas induk Aug 17, 2023 pm 01:33 PM

Menyelesaikan ralat PHP: Masalah yang dihadapi apabila mewarisi kelas induk Dalam PHP, pewarisan ialah ciri penting pengaturcaraan berorientasikan objek. Melalui pewarisan, kita boleh menggunakan semula kod sedia ada dan melanjutkan serta menambah baiknya tanpa mengubah suai kod asal. Walaupun warisan digunakan secara meluas dalam pembangunan, kadangkala anda mungkin menghadapi beberapa masalah ralat semasa mewarisi daripada kelas induk Artikel ini akan menumpukan pada menyelesaikan masalah biasa yang dihadapi apabila mewarisi daripada kelas induk dan memberikan contoh kod yang sepadan. Soalan 1: Kelas induk tidak ditemui Semasa proses mewarisi kelas induk, jika sistem tidak

Masalah keupayaan generalisasi model pembelajaran mesin Masalah keupayaan generalisasi model pembelajaran mesin Oct 08, 2023 am 10:46 AM

Keupayaan generalisasi model pembelajaran mesin memerlukan contoh kod khusus Dengan pembangunan dan aplikasi pembelajaran mesin yang semakin meluas, orang ramai semakin memberi perhatian kepada keupayaan generalisasi model pembelajaran mesin. Keupayaan generalisasi merujuk kepada keupayaan ramalan model pembelajaran mesin pada data tidak berlabel, dan juga boleh difahami sebagai kebolehsuaian model dalam dunia sebenar. Model pembelajaran mesin yang baik harus mempunyai keupayaan generalisasi yang tinggi dan dapat membuat ramalan yang tepat pada data baharu. Walau bagaimanapun, dalam aplikasi praktikal, kita sering menemui model yang berprestasi baik pada set latihan, tetapi gagal pada set ujian atau sebenar.

Masalah pemerolehan label dalam pembelajaran yang diselia dengan lemah Masalah pemerolehan label dalam pembelajaran yang diselia dengan lemah Oct 08, 2023 am 09:18 AM

Masalah pemerolehan label dalam pembelajaran yang diselia dengan lemah memerlukan contoh kod khusus Pengenalan: Pembelajaran diselia dengan lemah ialah kaedah pembelajaran mesin yang menggunakan label yang lemah untuk latihan. Berbeza daripada pembelajaran tradisional yang diselia, pembelajaran yang diselia dengan lemah hanya perlu menggunakan lebih sedikit label untuk melatih model, berbanding setiap sampel perlu mempunyai label yang tepat. Walau bagaimanapun, dalam pembelajaran yang diselia dengan lemah, cara mendapatkan maklumat berguna dengan tepat daripada label yang lemah adalah isu utama. Artikel ini akan memperkenalkan masalah pemerolehan label dalam pembelajaran yang diselia dengan lemah dan memberikan contoh kod khusus. Pengenalan kepada masalah pemerolehan label dalam pembelajaran yang diselia dengan lemah:

See all articles