基于JDK的动态代理原理分析

这篇文章解决三个问题:

  1. What 动态代理是什么

  2. How 动态代理怎么用

  3. Why 动态代理的原理

动态代理是什么?

动态代理是代理模式的一种具体实现,是指在程序运行期间,动态的生成目标对象的代理类(直接加载在内存中的字节码文件),实现对目标对象所有方法的增强。通过这种方式,我们可以在不改变(或无法改变)目标对象源码的情况下,对目标对象的方法执行前后进行干预。

动态代理怎么用?

首先,准备好我们需要代理的类和接口,因为JDK的动态代理是基于接口实现的,所以被代理的对象必须要有接口

/**
* SaySomething接口
*/
public interface SaySomething {

   public void sayHello();

   public void sayBye();
}
/**
* SaySomething的实现类
*/
public class SaySomethingImpl implements SaySomething {
   @Override
   public void sayHello() {
       System.out.println("Hello World");
  }

   @Override
   public void sayBye() {
       System.out.println("Bye Bye");
  }
}

按照动态代理的用法,需要自定义一个处理器,用来编写自定义逻辑,实现对被代理对象的增强。

自定义的处理器需要满足以下要求:

  • 需要实现InvocationHandler,重写invoke方法,在invoke方法中通过加入自定义逻辑,实现对目标对象的增强。

  • 需要持有一个成员变量,成员变量的是被代理对象的实例,通过构造参数传入。(用来支持反射调用被代理对象的方法)

  • 需要提供一个参数为被代理对象接口类的有参构造。(用来支持反射调用被代理对象的方法)

/**
* 自定义的处理器,用来编写自定义逻辑,实现对被代理对象的增强
*/
public class CustomHandler implements InvocationHandler {

   //需要有一个成员变量,成员变量为被代理对象,通过构造参数传入,用来支持方法的反射调用。
   private SaySomething obj;
   
   //需要有一个有参构造,通过构造函数将被代理对象的实例传入,用来支持方法的反射调用
   public CustomHandler(SaySomething obj) {
       this.obj = obj;
  }

   /**
    * proxy:动态生成的代理类对象com.sun.proxy.$Proxy0
    * method:被代理对象的真实的方法的Method对象
    * args:调用方法时的入参
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       //目标方法执行前的自定义逻辑处理
       System.out.println("-----before------");

       //执行目标对象的方法,使用反射来执行方法,反射需要传入目标对象,此时用到了成员变量obj。
       Object result = method.invoke(obj, args);

       //目标方法执行后的自定义逻辑处理
       System.out.println("-----after------");
       return result;
  }
}

这样我们就完成了自定义处理器的编写,同时在invoke方法中实现对了代理对象方法的增强,被代理类的所有方法的执行都会执行我们自定义的逻辑。

接下来,需要通过Proxy,newProxyInstance()方法来生成代理对象的实例,并进行方法调用测试。

public class JdkProxyTest {
   public static void main(String[] args) {
       //将生成的代理对象的字节码文件 保存到硬盘
       System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

       //被代理对象的实例
       SaySomething obj = new SaySomethingImpl();
       //通过构造函数,传入被代理对象的实例,生成处理器的实例
       InvocationHandler handler = new CustomHandler(obj);
       //通过Proxy.newProxyInstance方法,传入被代理对象Class对象、处理器实例,生成代理对象实例
       SaySomething proxyInstance = (SaySomething) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                                                                          new Class[]{SaySomething.class}, handler);
       //调用生成的代理对象的sayHello方法
       proxyInstance.sayHello();
       System.out.println("===================分割线==================");
       //调用生成的代理对象的sayBye方法
       proxyInstance.sayBye();
  }
}

运行main方法,查看控制台,大功告成。至此,我们已经完整的完成了一次动态代理的使用。

动态代理的原理

生成的proxyInstance对象到底是什么,为什么调用它的sayHello方法会执行CustomerHandler的invoke方法呢?

直接贴上proxyInstance的字节码文件,我们就会恍然大悟了...

//$Proxy0是SaySomething的实现类,重写了sayHello和sayBye方法
public final class $Proxy0 extends Proxy implements SaySomething {
   private static Method m1;
   private static Method m3;
   private static Method m2;
   private static Method m4;
   private static Method m0;

   public $Proxy0(InvocationHandler var1) throws {
       super(var1);
  }

   static {
       try {
           m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
           m3 = Class.forName("com.example.demo.hanmc.proxy.jdk.SaySomething").getMethod("sayHello");
           m2 = Class.forName("java.lang.Object").getMethod("toString");
           m4 = Class.forName("com.example.demo.hanmc.proxy.jdk.SaySomething").getMethod("sayBye");
           m0 = Class.forName("java.lang.Object").getMethod("hashCode");
      } catch (NoSuchMethodException var2) {
           throw new NoSuchMethodError(var2.getMessage());
      } catch (ClassNotFoundException var3) {
           throw new NoClassDefFoundError(var3.getMessage());
      }
  }
 
   //实现了接口的sayHello方法,在方法内部调用了CustomerHandler的invoke方法,同时传入了Method对象,
   //所以在CustomerHandler对象中可以通过mathod.invovke方法调用SyaSomthing的sayHello方法
   public final void sayHello() throws {
       try {
           //h是父类Proxy中的InvocationHandler对象,其实就是我们自定义的CustomHandler对象
           super.h.invoke(this, m3, (Object[])null);
      } catch (RuntimeException | Error var2) {
           throw var2;
      } catch (Throwable var3) {
           throw new UndeclaredThrowableException(var3);
      }
  }

   public final void sayBye() throws {
       try {
           super.h.invoke(this, m4, (Object[])null);
      } catch (RuntimeException | Error var2) {
           throw var2;
      } catch (Throwable var3) {
           throw new UndeclaredThrowableException(var3);
      }
  }
   public final int hashCode() throws {
      //忽略内容
  }
   public final boolean equals(Object var1) throws {
      //忽略内容
  }
   public final String toString() throws {
      //忽略内容
  }
}

看到了生成的代理对象的字节码文件,是不是一切都明白你了,原理竟然如此简单^_^

本文为个人学习整理,如有描述错误或者对相关内容感兴趣,欢迎评论或私信交流,一起讨论、共同进步。

基于JDK的动态代理原理分析的更多相关文章

  1. 基于 JDK 的动态代理机制

    『动态代理』其实源于设计模式中的代理模式,而代理模式就是使用代理对象完成用户请求,屏蔽用户对真实对象的访问. 举个最简单的例子,比如我们想要「FQ」访问国外网站,因为我们并没有墙掉所有国外的 IP,所 ...

  2. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  3. 设计模式学习——JAVA动态代理原理分析

    一.JDK动态代理执行过程 上一篇我们讲了JDK动态代理的简单使用,今天我们就来研究一下它的原理. 首先我们回忆下上一篇的代码: public class Main { public static v ...

  4. cglib源码分析(四):cglib 动态代理原理分析

    本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...

  5. 设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

    本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网 ...

  6. JDK和CGLIB动态代理原理

    1.JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. 2.CGLiB动态代 ...

  7. 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理

    本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...

  8. JDK和CGLIB动态代理原理区别

    JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...

  9. Java设计模式之JDK动态代理原理

    动态代理核心源码实现public Object getProxy() { //jdk 动态代理的使用方式 return Proxy.newProxyInstance( this.getClass(). ...

随机推荐

  1. CentOS7 部署黑客帝国代码雨

    1024程序猿的节日,搞一个黑客帝国画面玩玩 [root@localhost ~]# yum -y install ncurses-devel [root@localhost ~]# yum -y i ...

  2. JMM之Java中锁概念的分类总结

    在Java的并发编程中不可避免的涉及到锁.从不同维护可以将锁进行不同的分类,如下: 1.乐观锁和悲观锁(根据读写操作的比例划分) 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数 ...

  3. python数据结构:数组和列表

    线性结构有两种:数组和列表 array和list 其中list各项操作的时间复杂度如下 因为insert是在头部插入 所以列表所有元素后移,时间复杂度为O(n) remove移除列表中某个值的第一个匹 ...

  4. k8s初面考点ReplicaSet副本集极限9连击你懂了吗?

    k8s初面考点ReplicaSet副本集极限9连击你懂了吗? k8s考点灵魂拷问9连击 考点之简单描述一下k8s副本集ReplicaSet有什么作用? 考点之为什么ReplicaSet将取代Repli ...

  5. go面试集锦1

    目录 1.go优缺点 2.go中常量是怎么实现 3.go的值传递和引用 4.go struct能不能比较 5.go协程线程安全吗 6.go中关键字 7.make和new区别 8.defer 9.生产者 ...

  6. 【C# TAP 异步编程】四、SynchronizationContext 同步上下文|ExecutionContext

    一.同步上下文(SynchronizationContext)概述 由来 多线程程序在.net框架出现之前就已经存在了.这些程序通常需要一个线程将一个工作单元传递给另一个线程.Windows程序以消息 ...

  7. Linux主机内存评估手册-从零到无

    --时间:2020年10月22日 --作者:飞翔的小胖猪 文档基于Centos Linux操作系统作为生产服务器运行环境.实时的查看分析当前系统内存的使用情况是否存在内存瓶颈,结合应用及现行业务需求分 ...

  8. python面试_总结03_列表练习题

    1.列表练习题 完成下列列表相关的编程题,先运行下列的test函数,在完成每道题之后,都可以通过调用test函数检测所写函数对错. def test(got, expected): if got == ...

  9. C#10新特性-全局和隐式usings

    .NET 6发布后支持C#10,C# 10 向 C# 语言添加了很多功能,今天我们分享一下全局和隐式usings的使用: using 指令简化了使用命名空间的方式. C# 10 包括一个新的全局 us ...

  10. 『现学现忘』Docker相关概念 — 3、IaaS、SaaS、PaaS服务模式补充

    目录 1.IaaS服务模式 2.PaaS服务模式 (1)何时使用PaaS (2)PaaS的特点 (3)PaaS优势 3.SaaS服务模式 云计算通俗来说就是输入/输出和计算不在一个主机上.计算要用到计 ...