【Java动态代理如何实现】

在这里插入图片描述

✅Java动态代理如何实现

    • ✅JDK动态代理和Cglib动态代理的区别
  • ✅拓展知识仓
    • ✅静态代理和动态代理的区别
    • ✅动态代理的用途
    • ✅Spring AOP的实现方式
    • 📑JDK 动态代理的代码段
    • 📑Cglib动态代理的代码块
  • ✅注意事项:


在Java中,实现动态代理有两种方式:


1 . JDK动态代理 : Java.lang.reflect 包中的Proxy类和 InvocationHandler 接口提供了生成动态代理类的能力。


2 . Cglib动态代理 : Cglib (Code Generation Library) 是一个第三方代码生成类库,运行时在内存中动态生成一个了类对象从而实现对目标对象功能的扩展。


用一张图片看一下什么是动态代理(概念):

在这里插入图片描述


✅JDK动态代理和Cglib动态代理的区别


JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类就可以使用CGLIB实现。


Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的 interception (拦截)。


Calib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。


所以,使用JDK动态代理的对象必须实现一个或多个接口:而使用cgib代理的对象则无需实现接口,达到代理类无侵入。


✅拓展知识仓

✅静态代理和动态代理的区别


最大的区别就是静态代理是编译期确定的,但是动态代理却是运行期确定的。




同时,使用静态代理模式需要程序员手写很多代码,这个过程是比较浪费时间和精力的。一旦需要代理的类中方法比较多,或者需要同时代理多个对象的时候,这无疑会增加很大的复杂度。


反射是动态代理的实现方式之一。

✅动态代理的用途


Java的动态代理,在日常开发中可能并不经常使用,但是并不代表他不重要。Java的动态代理的最主要的用途就是应用在各种框架中。因为使用动态代理可以很方便的运行期生成代理类,通过代理类可以做很多事情,比如AOP,比如过滤器、拦截器等。


在我们平时使用的框架中,像 servlet 的 filter 、包括 spring 提供的 aop 以及 struts2 的拦截器都使用了动态代理功能。我们日常看到的mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部由动态代理的身影。


✅Spring AOP的实现方式


Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。


JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接 和 Proxy 类。


如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。


CGLIB (Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。


📑JDK 动态代理的代码段


public class UserServiceImpl implements UserService {@Overridepublic void add() {// TODO Auto-generated method stubSystem,out,println("--------------------add----------------------");}
}public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {super();this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {PerformanceMonior.begin(target.getClass().getlame( )+"+method.getlame());//System.out .println("-----------------begin “+method.getName()+"---------);Object result = method.invoke(target, args);//System.out.println("--------------end "+method.getName( )+"-----);PerformanceMonior.end();return result;}public Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this);}
}public static void main(string[] args) {UserService seryice = new UserServiceImpl();MyInvocationHandler handler = new MyInvocationHandler(service);UserService proxy = (UserService) handler.getProxy();proxy .add();
}

📑Cglib动态代理的代码块


public class UserServiceImpl implements UserService {@Overridepublic void add() {//TODO Auto-generated method stubSystem.out,println("--------------------add----------------------");}}public class CglibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class clazz) {//设置需要创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);//通过字节码技术动态创建子类实例return enhancer.create();}//实现MethodInterceptor接口方法public Object intercept(Object obj,Method method, Object[] args,MethodProxy proxy) throws Throwable {System.out.println("前置代理");//通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.printIn("后置代理");return result;}
}public class DoCGLib {public static void main(String[] args) {CglibProxy proxy = new CglibProxy();//通过生成子类的方式创建代理类UsenServiceImpl proxyImp = (UsenServiceImpl)proxy.getProxy(UserServiceImpl.class);proxyImp.add():}
}

✅注意事项:

JDK动态代理只能用于接口,不能用于类。

  • 动态代理只对方法调用有效,对字段访问和赋值无效
  • 如果目标对象抛出了异常,那么这个异常会被代理对象抛出,而不是在调用invoke方法时抛出

与CGLIB等其他代理技术的比较:


  • CGLIB:它是一个强大的高性能的代码生成库,可以扩展JAVA类和实现JAVA接口。它主要应用于高级OOP设计和应用,如AOP实现、缓存框架、事务管理等。
  • 两者的选择:如果你的目标是基于现有类的行为进行拦截或修改,可以使用CGLIB;如果目标是基于接口进行拦截或修改,那么应该使用JDK动态代理。

💡思考:


除了JDK动态代理和CGLIB,还有其他一些常用的代理技术,如Spring AOP、AspectJ等。这些技术提供了更高级的特性,如支持方法级别的拦截、支持运行时和编译时切面等。


// 导入java.lang.reflect包中的InvocationHandler和Proxy类  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  // 定义一个接口Hello  
interface Hello {  // 定义接口方法sayHello,没有实现  void sayHello();  
}  // 定义一个实现Hello接口的类HelloImpl  
class HelloImpl implements Hello {  // 实现接口方法sayHello  public void sayHello() {  System.out.println("Hello, world!");  }  
}  // 定义一个实现InvocationHandler接口的类DynamicProxyHandler  
class DynamicProxyHandler implements InvocationHandler {  // 私有成员变量obj,用来保存目标对象的引用  private Object obj;  // 构造方法,传入目标对象作为参数,赋值给obj成员变量  public DynamicProxyHandler(Object obj) { this.obj = obj; }  // 实现InvocationHandler接口的方法invoke,传入代理对象、方法、参数数组,返回值类型为Object  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {  // 在目标方法执行前输出"Before method call"  System.out.println("Before method call");  // 调用目标方法,传入参数args,返回值赋值给result变量  Object result = m.invoke(obj, args);   // 在目标方法执行后输出"After method call"  System.out.println("After method call");  // 返回目标方法的返回值或者异常(如果目标方法抛出了异常)  return result;    }  
}  public class DynamicProxyExample {  public static void main(String[] args) {  // 创建一个HelloImpl类的实例作为目标对象,并实现Hello接口的sayHello方法  Hello hello = new HelloImpl();   // 创建一个DynamicProxyHandler类的实例作为InvocationHandler,传入目标对象实例作为参数传入构造器中  InvocationHandler handler = new DynamicProxyHandler(hello);   /** 使用Proxy类的静态方法newProxyInstance创建代理对象实例,传入目标对象的类加载器、目标对象的接口数组以及* InvocationHandler实例作为参数传入构造器中。返回的是代理对象实例,类型为目标对象的接口类型。* 创建的代理对象实例可以直接像目标对象实例一样使用,只不过它实现了所有接口中的方法。* 所有这些方法的调用最终会调用到我们提供的InvocationHandler实例中对应的invoke方法中去处理。* 这样我们就能够在不修改原有代码的基础上为某个对象提供额外行为操作,* 也就是实现了在运行时动态扩展了某个类的行为功能操作,即实现了AOP的功能。* 这样就达到了在不修改原有代码的基础上扩展了某个类的行为功能操作的目的。* 比如可以在目标对象方法执行前后添加额外的逻辑操作处理。此处因为动态代理的目标是Hello接口,所以没有错误。*/ Hello proxy = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), new Class[] { Hello.class }, handler); }   
}

最后总结一下JDK动态代理的思想:

在这里插入图片描述

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

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

相关文章

从git上拉取代码---步骤

1、先进入gitlab地址 2、git clone 地址.git 3、cd 路径 4、git checkout dev 5、git checkout -b 自己的分支 (创建自己的分支)

Visual Studio2022配置ReSharper C++ 常用设置

如需安装免费的可以在下面留言,看到即回复 文章目录 Visual Studio2022配置ReSharper C 常用设置配置Visual Studio2022,使其能够按回车进行补全配置ReSharper C 设置自动弹出配置ReSharper C 的快捷键ReSharper C 去掉注释拼写使用中文注释 如何关闭新版…

SpringBoot整合jwt(小白入门)

本文项目所用版本为: https://blog.csdn.net/weixin_39570751/article/details/133386557 代码仓库: https://gitee.com/skyblue0678/springboot-demo 目录 什么是JWT JWT依赖 写一个jwt工具类 测试一下jwt 优化:将过期时间配置在文件中 答疑&…

Unitree H1,国内第一台能跑的全尺寸通用人形机器人

原创 | 文 BFT机器人 随着科技的不断发展,机器人技术已经逐渐渗透到各个领域,为人类的生产和生活带来了极大的便利。2023年可以说是通用人形机器人的高光之年,国内外不少机器人和科技团队纷纷发布了人形机器人原型机或产品规划,甚…

外贸中的很多跟想的不一样的事情

说说最近遇到的几个客户情况,以及对一些事情刷新的认知。 第一个客户姑且称为A吧,这个客户在询价的时候,产品的名称以及数量以还有走货的方式写的很清楚,客户A要的产品不是很多, 顶多算是个样品单。 一般情况下&…

PostgreSQL 可观测性最佳实践

简介 软件简述 PostgreSQL 是一种开源的关系型数据库管理系统 (RDBMS),它提供了许多可观测性选项,以确保数据库的稳定性和可靠性。 可观测性 可观测性(Observability)是指对数据库状态和操作进行监控和记录,以便在…

Linux操作系统——进程(四)进程切换与命令行参数

进程切换 概念引入 下面我们先了解几个概念: 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级 独立性: 多进程…

[论文阅读笔记28] 对比学习在多目标跟踪中的应用

这次做一篇2D多目标跟踪中使用对比学习的一些方法. 对比学习通过以最大化正负样本特征距离, 最小化正样本特征距离的方式来实现半监督或无监督训练. 这可以给训练MOT的外观特征网络提供一些启示. 使用对比学习做MOT的鼻祖应该是QDTrack, 本篇博客对QDTrack及其后续工作做一个总…

Linux OpenEuler(欧拉系统)无公网ip实现SSH远程连接

🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,…

研究生可以直接当教师吗

在教育领域,教师岗位的竞争日益激烈。许多即将毕业的研究生,尤其是教育学专业的,都怀揣着一个共同的疑问:我能不能一出校门就直接当教师? 成为教师并不只是有个学历那么简单。它需要具备扎实的教育学基础、良好的教学技…

Matlab:解非线性方程组

1、基于问题求解非线性方程组 例: xoptimvar(x,2); %将x定义为一个二元素优化变量 eq1exp(-exp(-(x(1)x(2))))x(2)*(1x(1)^2); %创建第一个方程作为优化等式表达式 eq2x(1)*cos(x(2))x(2)*sin(x(1))1/2; %创建第二个方程作为优化等式表达式 probe…

【数据结构和算法】找到最高海拔

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 前缀和的解题模板 2.1.1 最长递增子序列长度 2.1.2 寻找数组中第 k 大的元素 2.1.3 最长公共子序列…

fpga verilog rs232 发送模块实现

RS-232是一种串行通信协议,用于在计算机和其他外部设备之间进行数据传输。RS-232定义了电气特性、信号级别、机械特性和传输速率等规范,为串行通信提供了一种标准化的接口。 RS-232通常使用DB9连接器,用于传输和接收数据、控制信号以及地线连…

12月25日作业

串口发送控制命令&#xff0c;实现一些外设LED 风扇 uart4.c #include "uart4.h"void uart4_config() {//1.使能GPIOB\GPIOG\UART4外设时钟RCC->MP_AHB4ENSETR | (0x1 << 1);RCC->MP_AHB4ENSETR | (0x1 << 6);RCC->MP_APB1ENSETR | (0x1 <…

边缘计算云边端全览—边缘计算系统设计与实践【文末送书-10】

文章目录 一.边缘计算1.1边缘计算的典型应用 二.边缘计算 VS 云计算三.边缘计算系统设计与实践【文末送书-10】3.1 粉丝福利&#xff1a;文末推荐与福利免费包邮送书&#xff01; 一.边缘计算 边缘计算是指在靠近物或数据源头的一侧&#xff0c;采用网络、计算、存储、应用核心…

STM32CubeIDE在使用freertos的情况下空间占用情况

因为STM32F103C8T6芯片的存储空间太小了&#xff0c;只有64K&#xff0c;在使用STM32CubeIDE的freertos情况下空间占用情况做个简单的测试&#xff1a; 不使用程序优化&#xff1a; 空间占用情况&#xff1a; 如果使用浮点运算功能的printf&#xff1a; 这个使用空间占用更大…

Isaac Sim 仿真机器人urdf文件导入

本教程展示如何在 Omniverse Isaac Sim 中导入 urdf 一. 使用内置插件导入urdf 安装urdf 插件 方法是转到“window”->“Extensions” 搜索框中输入urdf, 并启用 通过转至Isaac Utils -> Workflows -> URDF Importer菜单来访问 urdf 扩展。 表格中的 1,2,3 对应着…

利用MATLAB设计一个(2,1,7)卷积码编译码器

1、条件&#xff1a; 输入数字信号&#xff0c;可以随机产生&#xff0c;也可手动输入 2、要求&#xff1a; &#xff08;1&#xff09;能显示编码树、网格图或状态转移图三者之一&#xff1b; &#xff08;2&#xff09;根据输入数字信号编码生成卷积码并显示&#xf…

堡垒机的演变过程

堡垒机的概念源自跳板机&#xff08;前置机&#xff09;。早在20世纪90年代末21世纪初期&#xff0c;部分中大型企业为了能对运维人员的远程登录进行集中管理&#xff0c;会在机房部署一台跳板机。跳板机其实就是一台unix/windows操作系统的服务器。并且所有运维人员都需要先远…