文档章节

java.lang.reflect.InvocationHandler中invoke()方法调用时机

程序猿猴
 程序猿猴
发布于 2015/08/18 14:45
字数 1686
阅读 872
收藏 5
点赞 0
评论 0

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


invoke方法的完整形式如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
    {  
  
        method.invoke(obj, args);  
  
        return null;  
    }


首先猜测一下:

        method是调用的方法,即需要执行的方法;

        args是方法的参数;

        proxy,这个参数是什么?

以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。

查看JDK文档中对Proxy有如下说明:

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参数传递的即是代理类的实例。


为了方便说明,这里写一个简单的栗子来实现动态代理,不知道如何创建的童鞋请先了解一下

//抽象角色(动态代理只能代理接口)  
public interface Subject {  
      
    public void request();  
}
//真实角色:实现了Subject的request()方法  
public class RealSubject implements Subject{  
      
    public void request(){  
        System.out.println("From real subject.");  
    }  
}
//实现了InvocationHandler  
public class DynamicSubject implements InvocationHandler  
{  
    private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  
  
    public DynamicSubject()  
    {  
    }  
  
    public DynamicSubject(Object obj)  
    {  
        this.obj = obj;  
    }  
  
    //这个方法不是我们显示的去调用  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
    {  
        System.out.println("before calling " + method);  
  
        method.invoke(obj, args);  
  
        System.out.println("after calling " + method);  
  
        return null;  
    }  
  
}
//客户端:生成代理实例,并调用了request()方法  
public class Client {  
  
    public static void main(String[] args) throws Throwable{  
        // TODO Auto-generated method stub  
  
        Subject rs=new RealSubject();//这里指定被代理类  
        InvocationHandler ds=new DynamicSubject(rs);  
        Class<?> cls=rs.getClass();  
          
        //以下是一次性生成代理  
          
        Subject subject=(Subject) Proxy.newProxyInstance(  
                cls.getClassLoader(),cls.getInterfaces(), ds);  
          
        //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  
        System.out.println(subject instanceof Proxy);  
          
        //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  
        System.out.println("subject的Class类是:"+subject.getClass().toString());  
          
        System.out.print("subject中的属性有:");  
          
        Field[] field=subject.getClass().getDeclaredFields();  
        for(Field f:field){  
            System.out.print(f.getName()+", ");  
        }  
          
        System.out.print("\n"+"subject中的方法有:");  
          
        Method[] method=subject.getClass().getDeclaredMethods();  
          
        for(Method m:method){  
            System.out.print(m.getName()+", ");  
        }  
          
        System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());  
          
        System.out.print("\n"+"subject实现的接口是:");  
          
        Class<?>[] interfaces=subject.getClass().getInterfaces();  
          
        for(Class<?> i:interfaces){  
            System.out.print(i.getName()+", ");  
        }  
  
        System.out.println("\n\n"+"运行结果为:");  
        subject.request();  
    }  
}


运行结果如下:

true  
subject的Class类是:class $Proxy0  
subject中的属性有:m1, m3, m0, m2,   
subject中的方法有:request, hashCode, equals, toString,   
subject的父类是:class java.lang.reflect.Proxy  
subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,   
  
运行结果为:  
before calling public abstract void ***.Subject.request()  
From real subject.  
after calling public abstract void ***.Subject.request()


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


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

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

public static Object newProxyInstance(ClassLoader loader,  
        Class<?>[] interfaces,  
        InvocationHandler h)  
throws IllegalArgumentException  
{  
    if (h == null) {  
        throw new NullPointerException();  
    }  
  
    /* 
     * Look up or generate the designated proxy class. 
     */  
    Class cl = getProxyClass(loader, interfaces);  
  
    /* 
     * Invoke its constructor with the designated invocation handler. 
     */  
    try {  
           /* 
            * Proxy源码开始有这样的定义: 
            * private final static Class[] constructorParams = { InvocationHandler.class }; 
            * cons即是形参为InvocationHandler类型的构造方法 
           */  
        Constructor cons = cl.getConstructor(constructorParams);  
        return (Object) cons.newInstance(new Object[] { h });  
    } catch (NoSuchMethodException e) {  
        throw new InternalError(e.toString());  
    } catch (IllegalAccessException e) {  
        throw new InternalError(e.toString());  
    } catch (InstantiationException e) {  
        throw new InternalError(e.toString());  
    } catch (InvocationTargetException e) {  
        throw new InternalError(e.toString());  
    }  
}


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

class Proxy{  
    InvocationHandler h=null;  
    protected Proxy(InvocationHandler h) {  
        this.h = h;  
    }  
    ...  
}


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

public final class $Proxy0 extends Proxy implements Subject {  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  
  
    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("***.RealSubject").getMethod("request",  
                    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());  
        }  
    } //static  
  
    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  
  
    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    public final void request() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
}


接着把得到的$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方法中使用反射来获取关于代理类的一些信息吧。

本文转载自:http://paddy-w.iteye.com/blog/841798

共有 人打赏支持
程序猿猴
粉丝 4
博文 1
码字总数 0
作品 0
青岛
程序员
InvocationHandler的invoke方法如何被调用?

在客户类(即以下代码的Client类),语句Manager managerProxy = (Manager) Proxy.newProxyInstance(managerImpl.getClass().getClassLoader(), managerImpl.getClass().getInterfaces(), se......

姚情侣 ⋅ 2013/04/29 ⋅ 0

java 动态代理

/ 相亲接口 @author zhengt @time Jun 3, 2095 3:13:03 PM */ public interface XiangQinInterface { / 相亲方法 / public void xiangQin(); } / 张三相亲实现类 @author zhengt @time Jun 3......

Zero零_度 ⋅ 2015/04/08 ⋅ 0

Spring/Boot/Cloud系列知识(2)——代理模式

代理模式是23种设计模式中的一种,属于一种结构模式。用一句大白话解释这个设计模式:当外部调用者调用A来完成一件事情/一个动作时,并不直接调用A本身,而是调用一个代理者,并再由代理者负...

yinwenjie ⋅ 2017/09/15 ⋅ 0

Java动态代理的两种实现方法

AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函...

odlingyjbe ⋅ 2016/05/22 ⋅ 0

设计模式3——Proxy设计模式

Proxy代理设计模式是一种控制对象访问的设计模式,类似于网络代理,网络代理机制如下图: Proxy代理设计模式机制如下: 代理模式UML图如下: 代理模式顺序图如下: 客户端程序通过代理程序来...

小米米儿小 ⋅ 2013/12/06 ⋅ 0

代理模式&动态代理

动态代理的用途: 动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换,动态代理可以更好的解耦合 增强有3个手段 1. 继承 被增强...

wall--e ⋅ 2016/04/14 ⋅ 0

JDK动态代理的简单实现

先理一下动态代理实现的思路: 实现功能: 自己定义一个类 Proxy, 通过Proxy的静态方法 newProxyInstance(Class<T> intface,InvocationHandler h)返回代理对象, intface: 被代理类的接口对象,...

wall--e ⋅ 2016/04/27 ⋅ 0

静态代理与静态代理

1 代理模式及概念 代理模式是给某个对象提供一个代理对象,并由代理对象控制对原始对象的引用。如下图: 代理类和委托类有共同的父类,这样在任何使用原始对象(委托对象)的地方都可用代理对...

林中漫步 ⋅ 2016/05/15 ⋅ 0

Hadoop源码分析----RPC反射机制

有了Client 和Server,很自然就能RPC 啦。下面轮到RPC.java 啦。 一般来说,分布式对象一般都会要求根据接口生成存根和框架。如CORBA,可以通过IDL,生成存根和框架。但是,在 org.apache.h...

超人学院 ⋅ 2015/05/07 ⋅ 0

Java反射笔记

Java反射机制是指在运行状态中,对于任意一个类,都知道这个类的所有属性和方法;对于任意一个对象,都能调用它的属性和方法,反射功能十分的强大,但是使用反射的成本比较高。 Sun公司提供的...

黄步欢 ⋅ 2017/05/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 29分钟前 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 37分钟前 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 43分钟前 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 53分钟前 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

容器之查看minikue的environment——minikube的环境信息

执行如下命令 mjduan@mjduandeMacBook-Pro:~/Docker % minikube docker-envexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/U......

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

mysql远程连接不上

设置了root所有hosts远程登录,可是远程登录还是失败,原因可能如下: 登录本地数据库 mysql -uroot -p123456 查询用户表 mysql> select user,host,password from mysql.user; 删除密码为空的...

冰公子 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部