InvocationHandler中invoke()方法的调用问题

InvocationHandler的妙用 (阁瑞钛伦特软件—九耶实训) InvocationHandler的妙用 InvocationHandler翻译成中文就是调用处理器,位于java.lang.reflect包下,经常在反射编程时使用。但是为什么使用?何时使用?如何使用?是很多Java初学者经常困惑的地方,下面通过一个DAO编程的案例来说明它的用法。案例需求是这样的:使用JDBC编程访问MySQL数据库中的职员表,实施职员数据的新增和修改操作。因此定义EmployeeDao接口: 并给出通常方式的Dao接口的实现类: ... 阅读详情
以下的内容部分参考了网络上的内容,在此对原作者表示感谢! 

        Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。 
        首先,invoke方法的完整形式如下: 
Java代码   收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  2.     {  
  3.   
  4.         method.invoke(obj, args);  
  5.   
  6.         return null;  
  7.     }  

        首先猜测一下,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.       
  4.     public void request();  
  5. }  

Java代码   收藏代码
  1. //真实角色:实现了Subject的request()方法  
  2. public class RealSubject implements Subject{  
  3.       
  4.     public void request(){  
  5.         System.out.println("From real subject.");  
  6.     }  
  7. }  

Java代码   收藏代码
  1. //实现了InvocationHandler  
  2. public class DynamicSubject implements InvocationHandler  
  3. {  
  4.     private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  
  5.   
  6.     public DynamicSubject()  
  7.     {  
  8.     }  
  9.   
  10.     public DynamicSubject(Object obj)  
  11.     {  
  12.         this.obj = obj;  
  13.     }  
  14.   
  15.     //这个方法不是我们显示的去调用  
  16.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  17.     {  
  18.         System.out.println("before calling " + method);  
  19.   
  20.         method.invoke(obj, args);  
  21.   
  22.         System.out.println("after calling " + method);  
  23.   
  24.         return null;  
  25.     }  
  26.   
  27. }  

Java代码   收藏代码
  1. //客户端:生成代理实例,并调用了request()方法  
  2. public class Client {  
  3.   
  4.     public static void main(String[] args) throws Throwable{  
  5.         // TODO Auto-generated method stub  
  6.   
  7.         Subject rs=new RealSubject();//这里指定被代理类  
  8.         InvocationHandler ds=new DynamicSubject(rs);  
  9.         Class<?> cls=rs.getClass();  
  10.           
  11.         //以下是一次性生成代理  
  12.           
  13.         Subject subject=(Subject) Proxy.newProxyInstance(  
  14.                 cls.getClassLoader(),cls.getInterfaces(), ds);  
  15.           
  16.         //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  
  17.         System.out.println(subject instanceof Proxy);  
  18.           
  19.         //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  
  20.         System.out.println("subject的Class类是:"+subject.getClass().toString());  
  21.           
  22.         System.out.print("subject中的属性有:");  
  23.           
  24.         Field[] field=subject.getClass().getDeclaredFields();  
  25.         for(Field f:field){  
  26.             System.out.print(f.getName()+", ");  
  27.         }  
  28.           
  29.         System.out.print("\n"+"subject中的方法有:");  
  30.           
  31.         Method[] method=subject.getClass().getDeclaredMethods();  
  32.           
  33.         for(Method m:method){  
  34.             System.out.print(m.getName()+", ");  
  35.         }  
  36.           
  37.         System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());  
  38.           
  39.         System.out.print("\n"+"subject实现的接口是:");  
  40.           
  41.         Class<?>[] interfaces=subject.getClass().getInterfaces();  
  42.           
  43.         for(Class<?> i:interfaces){  
  44.             System.out.print(i.getName()+", ");  
  45.         }  
  46.   
  47.         System.out.println("\n\n"+"运行结果为:");  
  48.         subject.request();  
  49.     }  
  50. }  

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. 运行结果为:  
  10. before calling public abstract void ***.Subject.request()  
  11. From real subject.  
  12. 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.     /* 
  11.      * Look up or generate the designated proxy class. 
  12.      */  
  13.     Class cl = getProxyClass(loader, interfaces);  
  14.   
  15.     /* 
  16.      * Invoke its constructor with the designated invocation handler. 
  17.      */  
  18.     try {  
  19.            /* 
  20.             * Proxy源码开始有这样的定义: 
  21.             * private final static Class[] constructorParams = { InvocationHandler.class }; 
  22.             * cons即是形参为InvocationHandler类型的构造方法 
  23.            */  
  24.         Constructor cons = cl.getConstructor(constructorParams);  
  25.         return (Object) cons.newInstance(new Object[] { h });  
  26.     } catch (NoSuchMethodException e) {  
  27.         throw new InternalError(e.toString());  
  28.     } catch (IllegalAccessException e) {  
  29.         throw new InternalError(e.toString());  
  30.     } catch (InstantiationException e) {  
  31.         throw new InternalError(e.toString());  
  32.     } catch (InvocationTargetException e) {  
  33.         throw new InternalError(e.toString());  
  34.     }  
  35. }  


        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.   
  7.     static {  
  8.         try {  
  9.             m1 = Class.forName("java.lang.Object").getMethod("equals",  
  10.                     new Class[] { Class.forName("java.lang.Object") });  
  11.   
  12.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
  13.                     new Class[0]);  
  14.   
  15.             m3 = Class.forName("***.RealSubject").getMethod("request",  
  16.                     new Class[0]);  
  17.   
  18.             m2 = Class.forName("java.lang.Object").getMethod("toString",  
  19.                     new Class[0]);  
  20.   
  21.         } catch (NoSuchMethodException nosuchmethodexception) {  
  22.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  23.         } catch (ClassNotFoundException classnotfoundexception) {  
  24.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  25.         }  
  26.     } //static  
  27.   
  28.     public $Proxy0(InvocationHandler invocationhandler) {  
  29.         super(invocationhandler);  
  30.     }  
  31.   
  32.     @Override  
  33.     public final boolean equals(Object obj) {  
  34.         try {  
  35.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
  36.         } catch (Throwable throwable) {  
  37.             throw new UndeclaredThrowableException(throwable);  
  38.         }  
  39.     }  
  40.   
  41.     @Override  
  42.     public final int hashCode() {  
  43.         try {  
  44.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  
  45.         } catch (Throwable throwable) {  
  46.             throw new UndeclaredThrowableException(throwable);  
  47.         }  
  48.     }  
  49.   
  50.     public final void request() {  
  51.         try {  
  52.             super.h.invoke(this, m3, null);  
  53.             return;  
  54.         } catch (Error e) {  
  55.         } catch (Throwable throwable) {  
  56.             throw new UndeclaredThrowableException(throwable);  
  57.         }  
  58.     }  
  59.   
  60.     @Override  
  61.     public final String toString() {  
  62.         try {  
  63.             return (String) super.h.invoke(this, m2, null);  
  64.         } catch (Throwable throwable) {  
  65.             throw new UndeclaredThrowableException(throwable);  
  66.         }  
  67.     }  
  68. }  


        接着把得到的$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方法中使用反射来获取关于代理类的一些信息吧。
代理二:深入研究InvocationHandler、动态代理类工作原理、实现AOP框架 -------android培训、Java培训、期待与您交流!------- 9  通过实例深入研究InvocationHandler 先看如下代码: import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method 阅读详情

相关推荐

Open UI5 源代码解析之536:Field.designtime.js

摘要:Field.designtime.js是SAP OpenUI5中mdc模块的设计时元数据文件,为sap.ui.mdc.field.Field控件提供设计环境支持。该文件通过属性过滤(如忽略value属性)、稳定元素标识获取、设计时动作定义(如设置面板入口)等机制,实现控件在设计工具中的可配置性。它采用变更等待机制确保设计修改的完整性,并通过工具启停控制避免运行时干扰。作为连接设计态与运行态的桥梁,该文件支持可视化配置字段行为(如跳转链接),同时保证变更在多环境部署中的稳定性。其设计体现了UI5对企业级

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 8489

Java动态代理InvocationHandler和Proxy学习笔记

1.InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。 看下官方文档对InvocationHandler接口的描述: {@code InvocationHandler} is the interface implement...

OceanSky的专栏 13万+

InvocationHandler, Proxy机制

NULL 博文链接:https://xienan123.iteye.com/blog/1008459

Java学习笔记(九)

是 Java 反射机制中的一个接口,属于包。它的主要作用是为动态代理提供处理方法调用的能力。当使用 Java 动态代理时,可以通过实现接口来定义在代理对象上调用方法时的行为。// 定义接口// 实现接口@Override// 自定义 InvocationHandler@Override// 前置增强:打印日志// 调用目标方法// 后置增强:打印结束信息// 创建动态代理实例// 调用代理实例的方法用于创建动态代理并处理方法调用。AOP。

m0_51431003的博客 2003

计算机控制液压同步顶升系统,PLC四点多点同步顶升系统,同步液压顶升系统

PLC同步顶升系统系统可分为5个部分:液压泵站、PLC计算机控制系统、液压终端、位移压力检测与人机界面操作系统,位移同步精度1mm,同步控制点数:4,8,12,16,20,40,80,99;产品特点:高速置零、低速顶升,升降速度可以任意控制;液压千斤顶内置式(或外置)位移、压力传感器、均载阀,保证了顶升系统在恶劣工作环境下的高可靠性;均载阀的过载保护功能,避免了多缸顶升时常见的胀缸事故;先进的进油...

weixin_30093045的博客 1432

SC-FDMA

<br />SC-FDMA(Single-carrier Frequency-Division Multiple Access,單載波分頻多工),是LTE的上行鏈路的主流多址。<br />SC-FDMA是單波載(Single-carrier),與OFDMA相比之下具有的較低的PAPR(峰值/平均功率比,peak-to-average power ratio),比多載波的PAPR低1-3dB左右(PAPR是由于多載波在频域叠加引起)。更低的PAPR可以使行動終端(mobile terminal)在發送功效方面

jod的专栏 3299

paddy.w InvocationHandler中invoke()方法的调用问题

以下的内容部分参考了网络上的内容,在此对原作者表示感谢! Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。 首先,invoke方法的完整形式如下: Java代码 public Object inv

baidu_20977425的专栏 500

jetson nano网络配置(有线、无线、USB)

注意:本节内容使用Jetson nano B01 版本或Jetson nano ALT 版本安装方式相同,在安装无线网卡等设备时,由于线材和设备比较脆弱,需要轻拿轻放,接口连接不上时,需要重新对准接口在进行连接,不要强行连接。Jetson nano 需要连接网络来进行更多的操作,本节就来讲解Jetson nano 的三种网络连接方法。有线:我们可以使用网线连接Jetson nano 的RJ45 接口,实现联网。

m0_73841621的博客 1万+
上一篇: InvocationHandler中invoke()方法的调用问题
下一篇: Spring AOP 实例解析(InvocationHandler 的invoke的proxy 参数作用)
banlucainiao
博客等级 码龄11年 319粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值