动态代理详解

动态代理

  • 一、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.mzph.cn/news/730181.shtml

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

相关文章

如何做代币分析:以 ARB 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;mingfootprint.network 数据源&#xff1a;ARB 代币仪表板 &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关的数据…

代码随想录算法训练营第三天|203.移除链表元素

题目&#xff1a; 203. 移除链表元素 已解答 简单 相关标签 相关企业 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val …

MySQL 存储过程(超详细)

一、什么是存储过程&#xff1f; 存储过程可称为过程化SQL语言&#xff0c;是在普通SQL语句的基础上增加了编程语言的特点&#xff0c;把数据操作语句(DML)和查询语句(DQL)组织在过程化代码中&#xff0c;通过逻辑判断、循环等操作实现复杂计算的程序语言。换句话说&#xff0c…

K倍区间(蓝桥杯)

文章目录 K倍区间题目描述前缀和数学优化代码部分解释 K倍区间 题目描述 给定一个长度为 N的数列&#xff0c;A1,A2,…AN&#xff0c;如果其中一段连续的子序列 Ai,Ai1,…Aj 之和是 K的倍数&#xff0c;我们就称这个区间 [i,j]是 K倍区间。 你能求出数列中总共有多少个 K倍区…

上海雷卯可以解决YPbPr/ YCbCr接口 ESD/EOS静电浪涌问题

YPbPr /YCbCr 接口传输的是视频信号&#xff0c;不传输音频信号。YPbPr 和 YCbCr 都是视频信号的颜色编码格式&#xff0c;多应用于机顶盒&#xff08;Set-top box&#xff09;,TV电视&#xff0c;投影仪&#xff0c;游戏机和DVD播放器。 YPbPr&#xff1a;是一种模拟视频接口…

05-调用API

上一篇&#xff1a; 04-JNI函数 调用 API 允许软件供应商将 Java VM 加载到任意本地应用程序中。供应商可以提供支持 Java 的应用程序&#xff0c;而无需链接 Java VM 源代码。 5.1 概述 下面的代码示例说明了如何使用调用 API 中的函数。在这个示例中&#xff0c;C 代码创建了…

Discuz论坛网站报错Discuz!Database Error(0)notconnect的解决办法

运营服务器大本营有段时间了&#xff0c;在运营期间遇到两次Discuz&#xff01;Database Error&#xff08;0&#xff09;notconnect报错&#xff0c;和你们分享遇到Discuz报错的解决办法&#xff0c;希望可以帮助到你。 首先网站报错&#xff08;0&#xff09;notconnect&…

【Web】浅聊Java反序列化之C3P0——JNDI注入利用

目录 简介 原理分析 EXP 前文&#xff1a;【Web】浅聊Java反序列化之C3P0——URLClassLoader利用 【Web】浅聊Java反序列化之C3P0——不出网Hex字节码加载利用 简介 出网的情况下&#xff0c;这个C3P0的Gadget可以和fastjson&#xff0c;Snake YAML , JYAML,Yamlbeans , …

如何防御udp攻击

UDP Flood是互联网上最经典的DDoS&#xff08;Distributed Denial of Service&#xff09;攻击之一。攻击者在短时间内向目标设备发送大量的UDP报文&#xff0c;导致链路拥塞甚至网络瘫痪。一般的UDP报文由攻击工具伪造&#xff0c;通常在数据段具备相同的特征&#xff0c;另一…

【嵌入式高级C语言】9:万能型链表懒人手册

文章目录 序言单向不循环链表拼图框架搭建 - Necessary功能拼图块1 创建链表头信息结构体 - Necessary2 链表头部插入 - Optional3 链表的遍历 - Optional4 链表的销毁 - Necessary5 链表头信息结构体销毁 - Necessary6 获取链表中节点的个数 - Optional7 链表尾部插入 - Optio…

网站开发之旅:从概念到实现

在我成为一名专业的网站开发者的过程中&#xff0c;我有幸参与了多个激动人心的项目。其中&#xff0c;一个我印象尤为深刻的经历是&#xff0c;开发一个名为“文案推荐网”的主题网站&#xff08;www.zimeiti.love&#xff09;。这个项目不仅让我深入了解了网站开发的各个方面…

excel常用操作备忘

目录 快捷键基础数据透视图统计某列的值出现的频数 数据有效性数据分列运算符顺序文本匹配中的通配符错误的类型&#xff08;常与IF嵌套使用&#xff09;函数RANK(num, ref, [order])MID(str, start, len)逻辑函数混合函数选取整列AVERAGEIF(range, criteria, average_range)TR…

【软件测试面试】银行项目测试面试题+答案(一)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试题&#xff1…

Unity类银河恶魔城学习记录9-2 P83 Explosive crystal源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Crystal_Skill_Controller using System.Collections; using System.Colle…

【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用

目录 一、Linux编译器-gcc/g1.1 gcc/g 使用方法1.2 程序的翻译过程1.3 链接 -- 动静态链接特点及区别 二、Linux调试器-gdb2.1 背景2.2 使用方法 三、Linux项目自动化构建工具-make/Makefile3.1 背景3.2 原理3.3 项目清理 一、Linux编译器-gcc/g 1.1 gcc/g 使用方法 格式&…

如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

用BIO实现tomcat

一、前言 本课程的难度较高&#xff0c;需要将Servlet原理和IO课程全部学完。 二、当前项目使用方式 (1).自定义servlet 自定义servlet需要实现WebServlet并且实现name和urlMapping 重启进行访问 http://localhost:8090/myServlet (2).自定义html 重启进行访问 http://loc…

项目的搭建与配置

vue create calendar_pro 选择如下配置选项 安装 vue3 支持 vue add vue-next package.json 关闭 eslint 检测。 vue.config.js 配置跨域同源策略。 const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true,devServe…

微服务:Docker篇

1. 初识Docker 1.1. 什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。 在数百上千台服务中重复部署&#xff0c;环境不一定一…

【计网】TCP协议安全与风险:深入探讨网络通信的基石

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 &#x1f310;前言 &#x1f512;正文 TCP (Transmission Control Protocol): UDP (User Datagram Protocol): HTTP (Hypertext Transfer …