前言

记录一篇好的文章
https://blog.csdn.net/zcc_0015/article/details/22695647
同时推荐狂神说的视频,对于动态代理讲解的很详细
https://www.bilibili.com/video/BV1WE411d7Dv?p=19

一、动态代理与静态代理的区别

(1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
(2)可以实现AOP编程,这是静态代理无法实现的;
(3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。
(4)动态代理的优势就是实现无侵入式的代码扩展。
静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

二、动态代理

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参数传递的即是代理类的实例。
为了方便说明,这里写一个简单的例子来实现动态代理。

package TestProxy;public interface Subject {public void request();
}
package TestProxy;//真实角色
public class RealSubject implements Subject{@Overridepublic void request() {System.out.println("From real subject");}
}
package TestProxy;import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;//实现InvocationHandler
//这两个注解 就生成了无参和全参的构造函数
@AllArgsConstructor
@NoArgsConstructor
public class DynamicSubject implements InvocationHandler {private Object obj;//这就是动态代理的好处 被封装的对象是Object类型 接受任意类型的对象//这个方法不是我们 显示的去调用@Override//而这个invoke方法就是在主函数 调用了sb.request方法时,就调用了Proxy类的request方法//进而调用了父类中的Proxy的invoke方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before calling "+method);//这个我还是不知道呀。。。//首先要理解的就是 method.invoke 我理解为 invoke method 也就是激活对应的方法//而这里 对应的的方法就是 realSubject中的getRequest//method一直都是 public abstract void TestProxy.Subject.request()method.invoke(obj,args);//再看看这两个System.out.println("obj是:"+obj);System.out.println("args是:"+args);//这个真的是个祸害 一用就会疯狂报错 还真如博主所说的// 看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧//这个的结果是com.sun.proxy.$Proxy0//可以将代理对象返回以进行连续调用System.out.println("proxy是:"+proxy.getClass().getName());System.out.println("after calling "+method);return null;}
}
package TestProxy;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//他生成了代理的实例 并且调用了request()方法
public class Client {public static void main(String[] args) {//这个是被代理的类Subject rs=new RealSubject();//生成代理类InvocationHandler ds=new DynamicSubject(rs);//这里是得到一系列的类Class<?> cls=rs.getClass();//下面是一次性生成代理//直接通过Proxy生成一个代理 并且其中三个参数 都是来自class的 classloader interfaces和一个invocationHandlerSubject sb= (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds);//sb instanceof Proxy这是啥意思//这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口//原来 instanceof真的可以和==一样System.out.println(sb instanceof Proxy);//这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口System.out.println("sbd的Class类是" +sb.getClass().toString());System.out.println("sb中的属性有: ");//这几个getFields代表什么意思呢 getFields是获得所有公共 public的字段,getDeclareFields//是获得所有声明的字段 包括private  因为Declared的意思就是 “已经声明”Field[] fields=sb.getClass().getFields();for(Field field:fields){System.out.println("getFields的对应:"+field.getName());}System.out.println("\n"+"sb中的方法有:");Method[] methods=sb.getClass().getMethods();for(Method method:methods){System.out.println("method的对应:"+method.getName());}System.out.println("\n"+"sb的父类是"+sb.getClass().getSuperclass());System.out.println("\n"+"sb实现的接口是: ");Class<?>[] interfaces=sb.getClass().getInterfaces();for(Class<?> interfaceName:interfaces){System.out.println("interface的对应是:"+interfaceName.getName());}System.out.println("\n"+"运行结果为:");//sb一直都是class com.sun.proxy.$Proxy0//他的父类是 class java.lang.reflect.Proxy//可以看一下 这两个有什么区别//还有个问题 就是 从什么时候开始 sb有这个request方法了。。//是因为sb是一个Subject类然后是代理类 强制转换成Subject类,所以他存在这个方法sb.request();}
}

运行结果

true
sbd的Class类是class com.sun.proxy.$Proxy0
sb中的属性有: sb中的方法有:
method的对应:equals
method的对应:toString
method的对应:hashCode
method的对应:request
method的对应:isProxyClass
method的对应:newProxyInstance
method的对应:getInvocationHandler
method的对应:getProxyClass
method的对应:wait
method的对应:wait
method的对应:wait
method的对应:getClass
method的对应:notify
method的对应:notifyAllsb的父类是class java.lang.reflect.Proxysb实现的接口是:
interface的对应是:TestProxy.Subject运行结果为:
before calling public abstract void TestProxy.Subject.request()
From real subject
obj是:TestProxy.RealSubject@1bbe9ba
args是:null
proxy是:com.sun.proxy.$Proxy0
after calling public abstract void TestProxy.Subject.request()

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

从以上代码和结果可以看出,我们并没有显示的调用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赋值,如下:
Java代码  收藏代码
class Proxy{  InvocationHandler h=null;  protected Proxy(InvocationHandler h) {  this.h = h;  }  ...
}  

来看一下这个继承了Proxy的P r o x y ProxyProxy的源代码:

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

接着把得到的P r o x y ProxyProxy实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了P r o x y ProxyProxy类中的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方法中使用反射来获取关于代理类的一些信息吧。

原文链接
https://blog.csdn.net/zcc_0015/article/details/22695647

java动态代理中的invoke方法相关推荐

  1. java动态代理中的invoke方法是如何被自动调用的

    相关文章:静态代理和动态代理的区别和联系 一.动态代理与静态代理的区别. (1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大: (2)可以实现AOP编程,这是静态代理无法实现的: (3) ...

  2. Java动态代理和Cglib动态代理最强王者阵容

    Python实战社群 Java实战社群 长按识别下方二维码,按需求添加 扫码关注添加客服 进Python社群▲ 扫码关注添加客服 进Java社群▲ 作者丨黎杜 来源丨非科班的科班(LDCldc1230 ...

  3. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)

    第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...

  4. Java动态代理源码详解

    一.概述   前言:本文除了讲解JDK动态代理及CGLIB动态代理实例和应用外,还会讲解JDK动态代理源码实现过程以及自己写一手个JDK动态代理等.   动态代理在很多底层框架中都会用得到,比如在Sp ...

  5. AspectJ和Spring AOP(java动态代理和CGLIB)简单介绍

    1.AOP介绍 什么是AOP:AOP就是面向切面编程.使用的背景: 1)我们的振隆维护着一千个方法,一天老板让振隆把这一千个方法都要加上事务代码(统一代码) 2)振隆咬咬牙,添加了一个新的方法,然后让 ...

  6. JAVA深入研究——Method的Invoke方法。

    在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java ...

  7. java动态代理和cglib动态代理

    本文转自: http://blog.csdn.net/leon709/article/details/9529307 动态代理应用广泛,spring,Struts等框架很多功能是通过动态代理,或者进一 ...

  8. java动态生成pdf文件的方法

    java动态生成pdf文件 文章目录 java动态生成pdf文件 前言 一.生成pdf模板 二.使用步骤 1.使用jar包 2.pdf实现方法 总结 前言 java开发过程中难免会遇到生成文件的需求, ...

  9. 大数据笔记(六)——HDFS的底层原理:JAVA动态代理和RPC

    一.Java的动态代理对象 实现代码如下: 1.接口类MyService package hdfs.proxy;public interface MyService {public void meth ...

最新文章

  1. ajax 提交订单,php-在Woocommerce 3中通过ajax提交并在结帐时创建订单
  2. 关于yield的一些资料
  3. mysql 存储过程创建以及调用
  4. 5招详解linux之openEuler /centos7防火墙基本使用指南
  5. Atitit 图像资料文档分类器 netpic image 网络图片与人像图片分类 微信图片分类 D:\0workspace\atiplat_img\src\com\attilax\img\ut
  6. 安装天文基本包:kapteyn和pyslalib
  7. 一步步学习微软InfoPath2010和SP2010--第三章节--表单设计基础:处理InfoPath布局、控件和视图(2)--添加一个布局和表格
  8. 图解排序算法之「冒泡排序」(详细解析)
  9. 清华同方台式计算机 U盘启动,清华同方台式机BIOS设置U盘启动方法
  10. 曾国藩36字深入解读-借智慧
  11. Android混合开发(一)——移动端与前端交互之JSBridge引入
  12. android局域网打印机共享文件夹,如何设置电脑使安卓手机能访问局域网共享的文件...
  13. 基于Vue.js的Web视频播放器插件vue-vam-video@1.3.6 正式发布
  14. 《C语言程序设计》江宝钏主编-习题8-4-复制字串!!!
  15. EAN13条形码了解,有c测试代码
  16. 百度地图API进行网页地点展示
  17. BDD之cucumber
  18. Flink检查点失败问题-汇总
  19. 【转载】JVM能够开启多少线程
  20. 阿里云负载均衡白名单自动修改脚本

热门文章

  1. 又是别人家的公司!乐视回应四天半工作制:体感很好,工作生活兼顾
  2. 【转】黑泽明的《七武士》
  3. 男人的一生,什么最重要?
  4. 1-7 CAD基础 多线(MLINE)
  5. 背包模型------------有依赖的背包问题
  6. MySQL操作之表的操作
  7. kernelbase.dll崩溃的处理_kernelbase.dll故障教程
  8. 分奇偶页后Word导出PDF目录页后多了空白页
  9. Linux组和权限管理
  10. win10电脑用lp地址连接共享打印机