动态代理详解

动态代理

  • 一、JDK动态代理
  • 二、CGLIB动态代理
  • 三、Javassist动态代理技术


  • 在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

一、JDK动态代理

  • DK动态代理技术:只能代理接口。

  • Java 动态代理实现相关类位于 java.lang.reflect 包,主要涉及两个类:

    • InvocationHandler 接口。它是代理实例的调用处理程序实现的接口,该接口中定义了如下方法:

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
      
      • invoke() 方法上有三个参数:
        • 第一个参数 proxy 表示代理类。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
        • 第二个参数 method 表示需要代理的方法。
        • 第三个参数 args 表示代理方法的参数数组。
    • Proxy 类。该类即为动态代理类,该类最常用的方法为:

      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
      
      • 这行代码做了两件事:
        • 第一件事:在内存中生成了代理类的字节码。
        • 第二件事:创建代理对象。
      • newProxyInstance() 方法有三个参数:
        • 第一个参数 loader 表示代理类的类加载器。。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
        • 第二个参数 interfaces 表示代理类实现的接口列表(与真实主题类的接口列表一致)。通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
        • 第三个参数 h 表示所指派的调用处理程序类。这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
  • 还是使用静态代理中的例子:一个接口和一个实现类。(参考上面的代码)

  • 要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;/*** 动态代理类**/public class TimerInvocationHandler implements InvocationHandler {private Object target; //需要代理的目标对象public TimerInvocationHandler() {}public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 目标执行之前增强。long begin = System.currentTimeMillis();// 调用目标对象的目标方法Object retValue = method.invoke(target, args);// 目标执行之后增强。long end = System.currentTimeMillis();System.out.println("耗费时常" + (end - begin) + "毫秒");// 返回目标对象方法的返回值。return retValue;}
    }
    
  • 编写 Client 测试程序:

    import service.OrderService;
    import service.OrderServiceImpl;public class Client {public static void main(String[] args) {// 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象OrderService ret = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TimerInvocationHandler(target));// 调用代理对象的代理方法ret.generate();ret.detail();ret.delete();//当你调用代理对象的代理方法的时候,注册在InvocationHandler接口中的invoke()方法会被调用。}
    }
    
  • 提供一个工具类:ProxyUtil,封装一个方法:

    package utils;import dynamic_proxy.TimerInvocationHandler;import java.lang.reflect.Proxy;public class ProxyUtil {private ProxyUtil(){}public static Object newProxyInstance(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TimerInvocationHandler(target));}
    }
    
  • 这样客户端代码就不需要写那么繁琐了:

    import service.OrderService;
    import service.OrderServiceImpl;
    import utils.ProxyUtil;public class Client {public static void main(String[] args) {// 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象OrderService ret = (OrderService) ProxyUtil.newProxyInstance(target);// 调用代理对象的代理方法ret.generate();ret.detail();ret.delete();//当你调用代理对象的代理方法的时候,注册在InvocationHandler接口中的invoke()方法会被调用。}
    }
    
  • Spring AOP 实现中使用了动态代理模式,使得代码中不存在与具体要用到的接口或类相关的引用。


二、CGLIB动态代理

  • CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
  • CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
  • 使用CGLIB,需要引入它的依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
  • 我们准备一个没有实现接口的类,如下:
public class UserService {public String  login(String username, String password) {try {Thread.sleep(13654);} catch (InterruptedException e) {e.printStackTrace();}if (username.equals("root") && password.equals("123456")) {return "success~";}return "username or password error!";}
}
  • 和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor编写MethodInterceptor接口实现类:
public class TimerMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 前增强long begin = System.currentTimeMillis();// 调用目标Object retValue = methodProxy.invokeSuper(target, objects);// 后增强long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");// 一定要返回return retValue;}
}
  • MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
    • 第一个参数:目标对象
    • 第二个参数:目标方法
    • 第三个参数:目标方法调用时的实参
    • 第四个参数:代理方法
  • 使用CGLIB在内存中为UserService类生成代理类,并创建对象:
public class Client {public static void main(String[] args) {// 创建字节码增强器Enhancer enhancer = new Enhancer();// 告诉cglib要继承哪个类enhancer.setSuperclass(UserService.class);// 设置回调接口enhancer.setCallback(new TimerMethodInterceptor());// 生成源码,编译class,加载到JVM,并创建代理对象UserService userServiceProxy = (UserService)enhancer.create();String ret = userServiceProxy.login("root", "123456");System.out.println(ret);}
}
  • 对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数:
    在这里插入图片描述
  • –add-opens java.base/java.lang=ALL-UNNAMED
  • –add-opens java.base/sun.net.util=ALL-UNNAMED

三、Javassist动态代理技术


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/522245.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全防御保护 Day7

1.因为FW1和FW2已处于双机热备状态&#xff0c;所以只需要对主设备进行配置即可。进入FW1的配置界面&#xff0c;选择“网络”界面&#xff0c;点击“IPsec”&#xff0c;进行IPsec通道的基本配置&#xff0c;这里选择的是“电信”链路。 2.完成上述配置后&#xff0c;进行待加…

Linux——基础IO

目录 前言 C语言文件操作 stdin & stdout & stderr 系统文件IO open close write read 文件描述符fd 重定向 dup2 Linux下一切皆文件 缓冲区 简易缓冲区 文件系统 磁盘 创建文件 删除文件 查看文件 软硬链接 软链接 硬链接 动静态库 静态库 …

网络安全: Kali Linux 使用 hping3 阻塞目标主机

目录 一、实验 1.环境 2. 物理机测试远程连接 Windows server 3.Kali Linux 使⽤ hping3 ⼯具 二、问题 1. 常见的 DoS ⽅式有哪些 2.hping3 测试⼯具的命令格式和选项参数 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统版本IP备注Kali Linux2024.…

第七届强网杯-PWN-【warmup】

文章目录 warmup libc 2.35检查IDA逆向maindeldelete_noteadd_noteshow_noteinput_numberread_16atoi __errno_location()相关解释prctl相关 思路高版本off by null利用技巧产生chunk extend泄露libc基地址泄露heap基地址修改放入tcachebin中的chunk的fd为stdout最后add两个chu…

Excel中筛选合并单元格后,只显示第一行怎么办?

Excel中筛选合并单元格后,只显示第一行怎么办? 参考链接:https://baijiahao.baidu.com/s?id=1736773058549439034&wfr=spider&for=pc 我们日常的Excel数据在展示的时候为了数据的清晰和美观往往部分相同的单元格进行合并,但是合并之后在筛选时会发现结果会显示异…

Nacos2.2.3之MySQL8.X持久化详细配置过程

Nacos2.2.3之MySQL8.X持久化详细配置过程 文章目录 Nacos2.2.3之MySQL8.X持久化详细配置过程1. 官网与下载1. 官网2. Naocs是什么&#xff1f;3. 下载 2. 安装与持久化配置1. 解压安装2. 创建数据库1. 连接数据库2. 创建nacos数据库3. 导入脚本4. 查看表 3. 持久化配置1. appli…

特性螺旋面的刀具设计记录

最近和成型类刀具杠上了&#xff0c;这不最近有小伙伴提供了两个比较特殊的螺旋面工件&#xff0c;通常称作阴、阳转子。具体形状如下&#xff1a; 阴转子 阴转子端面齿形没看出有什么特殊的&#xff0c;但是在轴剖面齿形是内凹的&#xff0c;这个是比较特殊的形式。 阳转子…

使用再生龙(Clonezilla)备份和还原Linux系统

1. 再生龙(Clonezilla)介绍&#xff1a; Clonezilla是一款开源的磁盘克隆和备份工具&#xff0c;它可以帮助用户快速、高效地克隆和备份硬盘或分区。Clonezilla提供了两种不同的版本&#xff1a;Clonezilla Live和Clonezilla SE(Server Edition)。 Clonezilla Live&#xff1a;…

5. Java内存模型JMM

文章目录 计算机硬件存储体系基于计算机存储结构的 JMM Java 内存模型 JavaMemoryModelJMM规范下的三大特性原子性可见性有序性 多线程对变量的读写过程读取过程 多线程先行发生原则 happens-beforex,y 的 case 说明happens-before 原则说明happens-before 大原则happens-befor…

VBA_MF系列技术资料1-395

MF系列VBA技术资料1-395 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

Devc++调试窗口,一闪而过,调试闪退解决办法

今天使用Dev C调试&#xff0c;发现直接给我闪退了&#xff0c;恼火 解决办法&#xff1a; 安装图片所示框框&#xff0c;从上到下选择&#xff1a; 退出重启&#xff0c;OK

【粉丝福利】探秘内部审计数字化之道:精准解析转型方法与成功路径

&#x1f33c;前言 内部审计是一种独立的、客观的确认和咨询活动&#xff0c;包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储&#xff0c;据此再对数据进行挖掘、分析和应…