InvocationHandle的invoke方法[通俗易懂]

InvocationHandle的invoke方法[通俗易懂]在学习代理模式中接触到了动态代理的相关内容,这是AOP的核心内容。先用一个例子说明动态代理的使用//接口(动态代理只能代理接口)publicinterfaceSubject{publicvoidrequest();}//接口实现:实现了Subject的request()方法publicclassRealSubjecti

大家好,又见面了,我是你们的朋友全栈君。

在学习代理模式中接触到了动态代理的相关内容,这是AOP的核心内容。先用一个例子说明 动态代理 的使用

//接口(动态代理只能代理接口)  
public interface Subject {  
      
    public void request();  
}  

//接口实现 :实现了Subject的request()方法  
public class RealSubject implements Subject{  
      
    public void request(){  
        System.out.println("From real subject.");  
    }  
} 

Inb

//实现了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;  
    }  
  
}  
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是Proxy的子类并且实现了Subject接口的一个类。
          
        Subject subject=(Subject) Proxy.newProxyInstance(  
                cls.getClassLoader(),cls.getInterfaces(), ds);       

}


以上就是动态代理的使用方法,有一点很奇怪,那就是invoke方法是如何被调用的呢? 可以看到在以上代码中并没有invoke的身影,那是因为有关invoke的调用时在Proxy类中,invoke方法并不是给我们显示的调用,查看源码我们可以看到。  

首先看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的子类,实现了指定的接口,返回的类叫做$Proxy0, 这个类包含了invoke的秘密! 请看源码:

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);  
        }  
    }  
}

该类中利用反射获取到了4个方法,Object类自带的equals,hasCode,toString ,以及接口Subject的唯一方法request,因此当我们在主函数中调用request.xxx(方法名),就是在$Proxy0类中调用相应方法,每个方法中都有 super.h.invoke(this,m3,null);        invoke函数在此起到了作用!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138722.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月30日 下午10:16
下一篇 2022年5月30日 下午10:36


相关推荐

  • 一次完整的http请求过程是怎样的?

    一次完整的http请求过程是怎样的?我们打开浏览器 在地址栏输入 www wukong com 几秒后浏览器打开悟空问答的页面 那么这几秒钟内发生了哪些事情 我就带大家一起看看完整的流程 解析 URL 浏览器首先会对输入的 URL 进行验证 如果不合法的时候 那么会把输入的文字传给默认的搜索引擎 比如你只在地址栏输入 悟空问答 几个字 如果 URL 通过验证 那么可以解析得到协议 http 或者 https 域名 wukong 资源 首页 等信息 DNS 查询 浏览器会先检查域名信息是否在缓存中 再检查域名是否在

    2026年3月16日
    3
  • 枚举类型enum用法(enum类型)

    【1】什么是枚举类?  实例(也叫对象)有限而固定的类,在Java里被称为枚举类。    例如,季节类,它只有4个实例(春、夏、秋、冬),并且这4个实例不会改变。可以用枚举类来表示:publicenumSeasonEnum{SPRING,SUMMER,FALL,WINTER;}    枚举类是一种特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以有自己的…

    2022年4月18日
    62
  • sql2008r2绿色版免安装_少女都市模拟器中文版激活成功教程版免费

    sql2008r2绿色版免安装_少女都市模拟器中文版激活成功教程版免费中文版本64位免激活成功教程SQLyog-12.5.064位中文激活成功教程版 百度云盘https://pan.baidu.com/s/1yvQ7f0esY8idqc_Ci3Su2Ahttps://pan.baidu.com/s/1mhFLVpm 原文:https://blog.csdn.net/odyssey1816/article/details/78801342…

    2026年2月26日
    6
  • matlab语法 axis on,matlabaxis

    matlab语法 axis on,matlabaxis编程语言中文网今天精心准备的是《matlabaxis》,下面是详解!Matlab里axis这个函数怎么用,举个例子!axis函数可以用于操作普通的坐标属性(轴的缩放和外观)。比如:axis([xminxmaxyminymax]):可以设置当前坐标轴x轴和y轴的限制范围axis([xminxmaxyminymaxzminzmaxcmincmax])可以设置x,y…

    2022年6月13日
    39
  • 服务器bios界面usb无线网卡,BIOS怎么开启无线网卡

    服务器bios界面usb无线网卡,BIOS怎么开启无线网卡

    2021年8月16日
    66
  • 在linux中hdparm作用,hdparm命令

    在linux中hdparm作用,hdparm命令hdparm 命令提供了一个命令行的接口用于读取和设置 IDE 或 SCSI 硬盘参数 语法 hdparm 选项 参数 选项 a 设定读取文件时 预先存入块区的分区数 若不加上选项 则显示目前的设定 A lt 0 或 1 gt 启动或关闭读取文件时的快取功能 c 设定 IDE32 位 I O 模式 C 检测 IDE 硬盘的电源管理模式 d lt 0 或 1 gt 设定磁盘的 DMA 模式 f 将内存缓冲区的数据写入硬

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号