invoke 方法介绍

想要知道 invoke方法为什么会自动调用我们先要来了解一下这个方法
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)首先 该方法来自于接口InvocationHandler ,该接口中仅有一个invoke方法 ,该方法中有三个参数
* @param   proxy the proxy instance that the method was invoked
这是接口源码注释中对第一个参数proxy的解释:方法被调用的代理实例   我们可以认为它就是一个代理实例但是代理又是什么呢?这时候可能有小伙伴蒙了。
代理实例其实是代理类本身的一个实例,下面是我自己写的一个动态代理小案例这是一个接口,动态代理必须有一个接口的存在
public interface PorxyInte {public void test();
}
这是一个实现了接口的实现类,写的有些简便,主要看起来清晰
public class ProImp implements PorxyInte {@Overridepublic void test() {System.out.println("test");}
}

这是测试类 通过了Proxy.newProxyInstance方法(具体作用等下说)返回了一个代理实例

  ProImp proImp = new ProImp();//创建了实例类对象PorxyInte porxyInte =(PorxyInte) Proxy.newProxyInstance(ProImp.class.getClassLoader(), ProImp.class.getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("..."+proxy.getClass().getName());//输出proxy对象的字节码文件名Method[] declaredMethods = this.getClass().getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod.getName());}method.invoke(proImp,null);return null;}});System.out.println(porxyInte.getClass().getName());//输出实现类的字节码文件名porxyInte.test();
com.sun.proxy.$Proxy0 // 实现类的字节码文件名
...com.sun.proxy.$Proxy0// 参数proxy的字节码文件名
在这边我们发现我自己定义的实现类和参数proxy的字节码文件名是一致的而根据前面对参数proxy的注释定义,我们不难得出$Proxy0就是一个代理类那么为什么我调用了.newProxyInstance方法会生成一个代理对象呢?

newProxyInstance方法介绍

这是package java.lang.reflect.proxy下的一个静态方法

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

我们需要注意的是它的三个参数

首先先介绍一下
第一个参数ClassLoader loader:类的加载器,传入我们自定义类的加载器

第二个参数Class<?>[] interfaces 注意很重要 这个参数是传入一个接口数组

第三个参数 h:类型是InvocationHandler,传入InvocationHandler接口的子类

在newProxyInstance方法中 所做的几件事情

!!重头戏来了

第一步:

  Class<?> cl = getProxyClass0(loader, intfs);

调用了getProxyClass0方法, 该方法 需要传入两个参数 一个是类加载器,一个是接口数组
在方法getProxyClass0 中 会创建出一个类$Proxy0 ,并且创建出这个内部类的引用返回
我通过生出的内部类文件,反编译出源码可以看下:

public final class $Proxy0 extends Proxyimplements PorxyInte
{private static Method m1;private static Method m3;private static Method m2;private static Method m0;// 看到这个构造方法, 调用了父类Proxy构造方法,将我们得invocationhandler实例传值过去//那这个构造方法触发的时机就是在 //  proxy得代理方法中 return cons.newInstance(new Object[]{h}); 通过反射来执行代理类得构造方法,//从而将invocationhandler实例从代理类赋值到父类,//也就是为什么在下面test方法中在super.h 这个不会产生空指针得重要原因public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}public final boolean equals(Object obj){try{return ((Boolean)super.h.invoke(this, m1, new Object[] {obj})).booleanValue();}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final void test(){try{super.h.invoke(this, m3, null);return;}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final String toString(){try{return (String)super.h.invoke(this, m2, null);}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final int hashCode(){try{return ((Integer)super.h.invoke(this, m0, null)).intValue();}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}static {try{m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {Class.forName("java.lang.Object")});m3 = Class.forName("cn.itcast.web.Test.PorxyInte").getMethod("test", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);}catch (NoSuchMethodException nosuchmethodexception){throw new NoSuchMethodError(nosuchmethodexception.getMessage());}catch (ClassNotFoundException classnotfoundexception){throw new NoClassDefFoundError(classnotfoundexception.getMessage());}}
}
注意:我们可以发现这个生成的类继承了proxy 并且实现了我自己定义的那个接口
这是因为在调用getProxyClass0 传入的接口数组,他会将这个数组遍历,并且实现这是他实现的test方法:
public final void test(){try{super.h.invoke(this, m3, null);return;}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}
      super不难理解  是proxy类对象h :但是h是什么我相信小伙伴们可能有点懵,这里留下个悬念,我们先把newProxyInstance方法聊完刚刚说过newProxyInstance第一步返回了$proxy引用对象第二步:
   //通过反射创建出构造器对象,并且传入constructorParams 类型参数private static final Class<?>[] constructorParams ={ InvocationHandler.class };final Constructor<?> cons = cl.getConstructor(constructorParams);//这是$proxy0的构造方法内部通过super(constructorParams)  创建父类对象//细心的小伙伴可能发现了,我们刚刚在$proxy中的h,在Proxy中进行了赋值//其实这个h就是我们在调用newProxyInstance方法是传进来的第三个参数final InvocationHandler ih = h;
最后一步:   return cons.newInstance(new Object[]{h});
通过反射 返回$proxy的实例这个就是我们在掉用newProxyInstance方法所做的事情

那么先回到我们之前的疑问?invoke方法为什么会自动运行?

我相信现在小伙伴们也能理解
总体流程: 我在测试类中通过返回的$proxy引用调用test方法

 porxyInte.test();

这时候会去调用$proxy方法中的test方法

 public final void test(){try{super.h.invoke(this, m3, null);return;}catch (Error ) { }catch (Throwable throwable){throw new UndeclaredThrowableException(throwable);}}

在test方法中
super代表父类Proxy,h代表父类中的变量,也就是我们传进来的InvocationHandler接口实例
然后又调用了实例中的invoke方法,这个时候是不是就一目了然,这就是为什么我们调用test方法,而 InvocationHandler中的invoke方法会自动运行的原因,这是因为在代理类中的test方法内容重新定义了

需要注意的点:
第一点:
很多刚接触代理的小萌新包括博主我 在刚开始的时候都一直代理类当做是我们的自定义实现类对象
代理类不是我们定义的类,而是Proxy创建的$proxy类
第二点:
invoke方法中的第一个参数Proxy,这边注意Proxy在invoke方法被赋值为this,this是谁呢?
他就是调用test方法的对象也就是我们的代理实例

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

到此为止,为什么动态代理invoke方法会自动运行的原因了
以上所有理解:都是博主自己的理解,如果有错误的地方,还请大家能一一指出,谢谢!!

jdk动态代理invoke方法自动运行原因相关推荐

  1. Java动态代理invoke方法自动执行的原因

    Java动态代理invoke方法自动执行的原因 在动态代理中,一般的是先调用Proxy.newProxyInstance()生成代理对象,然后通过调用代理对象的对应方法来实现增强,这里假设代理了被代理 ...

  2. Spring : Spring Aop JDK动态代理调用过程

    1.美图 2.概述 JDK动态代理参考 : JDK动态代理 3.源码 打开JdkDynamicAopProxy类,查看invoke方法: /*** Implementation of {@code I ...

  3. Jdk动态代理 底层源码分析

    前言 java动态代理主要有2种,Jdk动态代理.Cglib动态代理,本文主要讲解Jdk动态代理的使用.运行机制.以及源码分析.当spring没有手动开启Cglib动态代理,即:<aop:asp ...

  4. 08.jdk动态代理原理

    课程标题<jdk动态代理底层原理分析> 课程内容: 1.什么是代理模式 2.代理模式应用场景有哪些 3.代理模式实现方式有哪些 4.静态代理与动态代理区别 5.JDK动态代理原理分析 6. ...

  5. 接住喽????,送你个装逼的技能: JDK动态代理

    今天讲一个比较深层的知识点:JDK动态代理,这是个可以让小白在大咖面前装逼的神器,顺便送你一个代理模式的温习机会. 代理模式场景 为了引出动态代理的用法,我们先看看代理设计模式,这能让你了解JDK动态 ...

  6. 【拿来吧你】JDK动态代理

    java proxy 因为最近一段时间准备将这几年做的一些业务和技术做个沉淀,也自己造的一些轮子,发现时不时就会需要用到动态代理和反射,所以今天打算先对jdk的动态代理这部分内容做个简单的整理 介绍 ...

  7. spring jdk动态代理、Cglib动态代理和LoadTimeWeaver(LTW)的应用选择

    在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入.编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中:而类加载期织入则指通过特 ...

  8. JDK动态代理为什么必须要基于接口?

    前几天的时候,交流群里的小伙伴抛出了一个问题,为什么JDK的动态代理一定要基于接口实现呢? 好的安排,其实要想弄懂这个问题还是需要一些关于代理和反射的底层知识的,我们今天就盘一盘这个问题,走你~ 一个 ...

  9. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

最新文章

  1. MindArmour差分隐私
  2. python 类-python 类如何使用
  3. 文本编辑器实现光标定位的功能
  4. spring Boot打可执行的jar包
  5. 公用表表达式(CTE)WITH:树型查询、更新
  6. Android总结 之 AsyncTask(二)
  7. SAP C4C - Field creation not permitted in partner devel
  8. 【C#程序设计】教学讲义——第三章:C#语言基础
  9. php求北京时间方法,php怎么将获得的时间转换为北京时间
  10. 无需SherlockActionbar的SlidingMenu使用详解(二)——向Fragment中添加ViewPager和Tab
  11. [有限元] 四结点三角形单元和五结点三角形单元的形函数
  12. Nginx应用场景之静态服务器
  13. A*算法(三)算法实现
  14. linux基础练习,Linux基础指令练习
  15. mongo源码学习(四)服务入口点ServiceEntryPoint
  16. ubuntu linux桌面快捷方式,Ubuntu下生成桌面快捷方式
  17. MCS:连续随机变量——Erlang分布
  18. 史上最全最详细的APP运营推广策划方案
  19. Final Project 期末项目: PySnake
  20. 聊聊校招内推,意义/优缺点/如何抓住机会等

热门文章

  1. 二叉树的非递归遍历算法C语言实现(详细注释版)
  2. JS 打印——table表格
  3. 【AGC】管理签名密钥相关问题
  4. 修改apk二进制文件工具
  5. 【企业安全实战】数据库审计部署实践
  6. App 界面卡顿 如何优化 测试性能
  7. JS-对象/Date 日期对象/返回/设置年份方法/返回星期方法/返回(/设置)时间方法
  8. Unity3d C#实现显示计时器(游戏运行时间、录制时长等)功能(含项目源码)
  9. Excel工作表中最常用的10个经典技巧
  10. 谷歌小恐龙 T-Rex 之 物理外挂