Java 动态代理,invoke() 自动调用原理,invoke() 参数

本文介绍了静态代理和动态代理的概念,并且用代码、注释、图示和源码的方式来分析动态代理原理和invoke()自动调用原理。


学习动态代理,先从静态代理入手

静态代理

假如现在我们面临一个需求,把一个项目中所有访问数据库的方法都记录日志。最直接的方法是修改所有代码,在每个方法里面都加入写日志代码:

public class Dao {User findUserbyId(int id) {// 业务代码xxx;// 加入写日志代码xxx;}}

但是这样工作量会很大,并且模块之间是耦合的,比如下次的需求是修改记录日志的内容,那么又要去修改所有业务代码,显然这种方法是不可取的。

如果不可以修改业务代码呢?

很容易就想到静态代理,写代理类对业务代码进行调用,我们用老师教学生举例子:

/**
* 老师接口,老师可以教学生
*/
public interface Teacher { void teach();
}
/**
* 老师接口的实现类,具体实现 teach() 方法
*/
public class RealTeacher implements Teacher {@Overridepublic void teach() {System.out.println("I am a teacher, I am teaching!");}
}
/**
* 老师代理对象,增强 teach() 方法
*/
public class TeacherProxy implements Teacher {Teacher teacher = new RealTeacher();@Overridepublic void teach() {// 这里 dosomething() 可以实现一些代理类的功能dosomething();// 这里执行 RealTeacher 的 teach() 方法teacher.teach();dosomething();}
}
/**
* 测试代理类
*/
public class ProxyTest {public static void main(String args[]) {Teacher teacher = new TeacherProxy();// 这里执行代理类的 teach() 方法teacher.teach();}
}

用图示表示上面静态代理的代码,可以描述为:

从图片和代码可以看出,这里只是用一个新对象去包含原来的 RealTeacher 对象,看似多此一举,但是确实一个不改变原来的代码,对方法进行增强的好办法。但是,如果我们要增强很多方法,比如文章开头的例子,需要对所有访问数据库的方法添加日志记录,如果用静态代理,要为每一个对象编写代理累,代码量十分庞大,也难以维护。

动态代理

既然为每一个需要代理的对象(下文通称为目标对象)都编写相应的代理类很麻烦,那么能不能不写死代理对象和目标对象,只写一个代理对象就可以使用不同的目标对象呢?
答案是可以的,就是用动态代理。动态代理的代理对象不依赖于目标对象,不和目标对象耦合。其原理是把目标对象的类型信息(比如接口)作为参数,利用 Java 支持的反射来创建代理对象,这样,就解除了代理对象和目标对象的耦合,一个代理对象可以适用一些列的目标对象。用图表示为:

如上图所示,目标对象作为一个参数,动态代理得到这个参数之后分成三步走:

  1. 获得目标对象的接口信息和类加载器,然后根据这两个信息来反射得到代理类的 Class
  2. 获得目标对象的 Class
  3. 根据1 2两步获得的接口,代理类 Class,目标类 Class 实例化一个代理对象

这样, 就创建了一个代理对象。从图中可以看出,动态代理并没有和目标对象绑定,而是把目标对象作为一个参数。

JDK 提供了 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类来实现上述反射等功能,其中 java.lang.reflect.InvovationHandler 接口用于绑定目标对象需要增强的方法,java.lang.reflect.Proxy 提供静态方法 NewProxyInstance() 用于创建一个代理类实例,这样,这个代理类实习就被创建出来并且通过嵌入 InvocationHandler 绑定了目标类的方法。
上面的静态代理代码的例子改成动态代理:

/*** 老师接口,老师可以教学生*/
public interface Teacher {void teach();
}
/*** 老师接口的实现类,具体实现 teach() 方法*/
public class RealTeacher implements Teacher {@Overridepublic void teach() {// 老师正在教书System.out.println("I am a teacher, I am teaching!");}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*** 代理类,根据接口等信息动态地创建代理对象,其中 MyProxy 是 InvocationHandler 的实现类,用于绑定方法*/
public class MyProxy implements InvocationHandler {// tar用于接收目标类的参数private Object tar;// 绑定目标类,根据目标类的类加载器和接口创建代理对象,并返回public Object bind(Object target) {this.tar = target;// 注意:此处代码返回代理类return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);}// invoke() 方法用于方法的增强@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;// 执行一些方法System.out.println("Do something before");// 目标类的方法执行,这里实际上是执行目标对象的方法,// 也就是 bind(Object target)参数 object target 的方法result = method.invoke(tar, args);System.out.println("Do something after");return result;}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyTest {public static void main(String[] args) throws Throwable {MyProxy proxy = new MyProxy();// 传入目标对象,进行绑定Teacher teacher = (Teacher)proxy.bind(new RealTeacher());// 执行代理对象的方法teacher.teach();}
}

测试函数的输出应该为:

Do something before
I am a teacher, I am teaching!
Do something after

改写成内部类的方式可以更清楚的看到:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyTest {public static void main(String[] args) throws Throwable {// 这里能清楚地看到,目标对象 RealTeacher 是一个纯粹的参数Teacher teacher = (Teacher)getProxy(new RealTeacher());// teacher 是接口 Teacher 的一个实现类,// 完全从多态的角度也能想到执行的实际上是 RealTeacher 的 teach() 方法teacher.teach();}public static Object getProxy(Object target) throws Throwable{return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;System.out.println("Do something before");result = method.invoke(target, args);System.out.println("Do something after");return result;}});}
}

此源代码在 GitHub 上。
目标对象 RealTeacher 可以看成是一个纯粹的参数,不和代理类耦合。
在应用的时候,可以把具体实现过程忽略,把

Teacher teacher = (Teacher)getProxy(new RealTeacher());

看成一个黑箱,它输入一个目标类作为参数,返回一个实现了 Teacher 接口的代理类。到此为止,我们成功使用了动态代理,在没有和目标类 RealTeacher 绑定的情况下,创建了一个代理类,增强了 teach() 方法。

进一步深究代理类 teacher,invoke()

为了进一步探究生成的代理类 teacher 到底是什么一个情况,我们输出 teach 的一些类型信息:

    public static void main(String[] args) throws Throwable {// 这里能清楚地看到,目标对象 RealTeacher 是一个纯粹的参数Teacher teacher = (Teacher)getProxy(new RealTeacher());// teacher 是接口 Teacher 的一个实现类,// 完全从多态的角度也能想到执行的实际上是 RealTeacher 的 teach() 方法teacher.teach();System.out.println(teacher.getClass().getSuperclass());//输出是class java.lang.reflect.ProxyClass<?>[] interfaces = teacher.getClass().getInterfaces();for(Class<?> i : interfaces){System.out.println(i);// 输出是interface Teacher}Method[] methods = teacher.getClass().getDeclaredMethods();for (Method method : methods) {System.out.println(method);// 输出是:// public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)// public final java.lang.String com.sun.proxy.$Proxy0.toString()// public final int com.sun.proxy.$Proxy0.hashCode()// public final void com.sun.proxy.$Proxy0.teach() }}

由上述输出可以发现,

Teacher teacher = (Teacher)getProxy(new RealTeacher());

创建的代理类 teacher 实际是名字是 $Proxy0,它父类 Proxy,实现了 Teacher 接口,实现了 teach() 方法,所以代码

teacher.teach();

理所当然可以执行。

但是,我们知道代码 teacher.teach(); 最终执行了invoke()方法,那么这个teach()方法到底怎么和invoke()有关联的呢?

我们打开 teach 的方法 Proxy.newProxyInstance()源码的注释,关键的是这几行:

 * Returns a proxy instance for the specified interfaces* that dispatches method invocations to the specified invocation* handler.@param   loader the class loader to define the proxy class* @param   interfaces the list of interfaces for the proxy class*          to implement* @param   h the invocation handler to dispatch method invocations to* @return  a proxy instance with the specified invocation handler of a*          proxy class that is defined by the specified class loader*          and that implements the specified interfaces

再看 Proxy 类的注释:

* {@code Proxy} provides static methods for creating objects that act like instances* of interfaces but allow for customized method invocation.* To create a proxy instance for some interface {@code Foo}:* <pre>{@code*     InvocationHandler handler = new MyInvocationHandler(...);*     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),*                                          new Class<?>[] { Foo.class },*                                          handler);* }</pre>** <p>* A <em>proxy class</em> is a class created at runtime that implements a specified* list of interfaces, known as <em>proxy interfaces</em>. A <em>proxy instance</em>* is an instance of a proxy class.** Each proxy instance has an associated <i>invocation handler</i>* object, which implements the interface {@link InvocationHandler}.* A method invocation on a proxy instance through one of its proxy* interfaces will be dispatched to the {@link InvocationHandler#invoke* invoke} method of the instance's invocation handler, passing the proxy* instance, a {@code java.lang.reflect.Method} object identifying* the method that was invoked, and an array of type {@code Object}* containing the arguments.  The invocation handler processes the* encoded method invocation as appropriate and the result that it* returns will be returned as the result of the method invocation on* the proxy instance.*

由注释发现,Proxy.newProxyInstance()方法返回一个代理对象的实例,这个实例是和一个特殊的 InvocationHandler 相关的,再回看我们写的代码:

我们实例化的匿名内部类 InvocationHandler 正是有 invoke() 方法,所以刚才的问题:

那么这个teach()方法到底怎么和invoke()有关联的呢?

是因为代理对象实例 teacher 的父类是 Proxy,由 Proxy 去和我们实例化的匿名内部类 InvocationHandler 去关联

再看实例对象 teacher(也就是 $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 teach() {  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);  }  }
}

也验证了当执行 teacher.teach() 的时候,也就是执行 $Proxy0.teach() 的时候,会执行 super.h.invoke(),也就是执行父类 Proxy 的 invoke(),而父类 Proxy 是和 InvocationHandler 是关联的,总结为:

teacher.teach() --> $Proxy0.teach() --> super.h.invoke() --> proxy.invoke() --> invocationHandler.invoke()

图示可以表示为:

invoke() 的参数

从代码可以看出,invoke() 方法有三个参数:

  • Object proxy
  • Method method
  • Object[] args

我们在 invoke() 中输出 proxy 的名字:

return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 输出proxy名字System.out.println(proxy.getClass().getName());Object result = null;System.out.println("Do something before1");result = method.invoke(target, args);System.out.println("Do something after1");return result;}});

输出是:

上文说代理类名字是 $Proxy0,所以实际上 proxy 参数就是代指反射创建的代理类 $Proxy0,之所以不用 this,因为 this 代表的是 InvocationHandler() 这个匿名内部类的实例。

同样的,我们看 $Proxy0 的反编译代码:

这个代码中的this就是 $Proxy0 对象本身,也是作为 invoke() 的第一个参数,也就是 proxy 进行参数传递。所以也能得到 proxy 参数就是代指反射创建的代理类 $Proxy0 的结论。

对于第二个参数 method,我们看 Method 的注释:

 * A {@code Method} provides information about, and access to, a single method* on a class or interface.  The reflected method may be a class method* or an instance method (including an abstract method).

可以看出第二个参数 method 用于绑定的目标类的方法

第三个参数是方法的参数

总结

代理模式是一种不改变源代码对方法进行增强的好方法,但是静态代理代理类和目标类是绑定耦合的。

动态代理的特点是:

  • 利用目标类的接口信息,通过反射创建代理类
  • 可以把代理类固定下来,不因为业务代码量庞大而复杂,便于扩展、修改和维护。
  • 方便实现 RPC 和 AOP
  • 源码难以理解

Java 动态代理,invoke() 自动调用原理,invoke() 参数相关推荐

  1. java动态代理实现接口调用

    动态代理是基于接口实现的代理,mybatis就是用这个技术实现的 首先,创建一个最基本接口和实现类: interface UserManager {void addUser(); }class Use ...

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

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

  3. java动态代理三座大山InvocationHandler、newProxyInstance()和invoke()

    先分享记录些博主的文章: <SSM框架>2Spring详解-KuangStudy-文章 java动态代理Proxy.newProxyInstance_徐海兴的专栏-CSDN博客_proxy ...

  4. java动态代理实现与原理详细分析(代码层面解释了AOP的实现)

    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式    代理模式是常用的java设计模式, ...

  5. java动态代理实现与原理详细分析(【转载】By--- Gonjan )

    [转载]By---    Gonjan  关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式 ...

  6. Java动态代理的实现原理

    概述 AOP用到了两种动态代理来实现织入功能: jdk动态代理 cglib动态代理 比较: jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的.反射机制在生 ...

  7. (转)java动态代理实现与原理详细分析

    https://www.cnblogs.com/gonjan-blog/p/6685611.html 1.静态代理 静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类 ...

  8. 【java】java动态代理实现与原理详细分析

    文章目录 一.代理模式 二.静态代理 1.静态代理 2.静态代理简单实现 三.动态代理 1.动态代理 2.动态代理简单实现 四.动态代理原理分析 五.总结 关于Java中的动态代理,我们首先需要了解的 ...

  9. java动态代理实现与原理

    java动态代理实现与原理详细分析 关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式–代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式 代理模 ...

  10. Java 动态代理原理图解 (附:2种实现方式详细对比)

    动态代理在 Java 中有着广泛的应用,例如:Spring AOP 面向切面编程,Hibernate 数据查询.以及 RPC Dubbo 远程调用等,都有非常多的实际应用@mikechen 目录 Ja ...

最新文章

  1. 分享Silverlight/WPF/Windows Phone/HTML5一周学习导读(4月16日-4月22日)
  2. bitmap数据格式
  3. fake it until you make it
  4. 【新手提问导读】提问的艺术
  5. basic code
  6. 第二阶段个人总结10
  7. qscoj:喵哈哈村的冒菜店(线段树区间合并)
  8. QuantLib 金融计算——基本组件之 Date 类
  9. vux移动端UI组件库
  10. 信庭嵌入式工作室-ARM应用技术之体系结构应用(中)
  11. 按键拨号声音 DTMF MATLAB程序样例
  12. 怎么让termux运行Java_在安卓手机上通过 Termux 运行编译/运行 .NET 应用
  13. 低代码平台要怎么选?便宜其实也有好货!
  14. 接口测试面试题汇总(含答案)
  15. 如何在windows上安装虚拟机
  16. 从零开始学习VIO笔记 --- 第一讲:基础知识(四元数,李代数)
  17. 云服务器 网络端口,云服务器的端口号怎么看
  18. 多个西瓜视频怎么批量管理?
  19. 领域模型和领域对象的概念
  20. 【每日新闻】刘多:工业互联网对我国制造业高质量发展起到重要的推动作用...

热门文章

  1. VMware虚拟机中安装Win10系统
  2. matlab神经网络 股票预测模型,基于BP神经网络的股票预测模型
  3. win7和win10哪个好用
  4. Google Safe Browsing API的使用
  5. python将xls文件转换xlsx
  6. 选择FPGA,认识“她”。
  7. 在手机与计算机之间进行文件传输的方式,电脑和手机传输文件方法_电脑和手机如何传文件-win7之家...
  8. [数据挖掘笔记] KMeans豆瓣文本聚类
  9. 微信公众号第三方平台授权流程
  10. 爬虫加密算法实践(淘宝直播+百度指数)