首页 > 其他分享 >静态代理和动态代理

静态代理和动态代理

时间:2024-05-24 10:07:26浏览次数:17  
标签:调用 JDK 静态 代理 接口 CGLIB 动态

静态代理和动态代理

   一、什么是代理模式?

   代理模式(Proxy Pattern)给某一个对象提供一个代理,并由代理对象控制原对象的引用。代理对象在客户端和目标对象之间起到中介作用。

   代理模式可以分为静态代理和动态代理两种类型,而动态代理中又分为JDK动态代理和CGLIB代理两种。

   如下图:

   

   二、静态代理

   静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象。

   三、动态代理

   静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成。

   1. JDK动态代理

   1)实现原理

   在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

   Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理必须要有接口

  JDK的动态代理是基于反射实现。JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,回调我们实现的InvocationHandler接口的invoke方法。并且这个代理类是Proxy类的子类。这就是JDK动态代理大致的实现方式。
  2)JDK动态代理步骤

  3)代码示例

  定义一个学生管理的接口:

1 public interface IStudentService {
2 
3     void insertStudent();
4 
5     void deleteStudent();
6 }

  实现学生管理接口的类:

 1 public class StudentService implements IStudentService {
 2     public void insertStudent() {
 3         //添加学生
 4         System.out.println("添加学生");
 5     }
 6 
 7     public void deleteStudent() {
 8         //删除学生
 9         System.out.println("删除学生");
10     }
11 }

  定义一个JDK动态代理类:

 1 public class MyInvocationHandler implements InvocationHandler {
 2     private Object obj;
 3 
 4     public MyInvocationHandler(Object object){
 5         this.obj = object;
 6     }
 7 
 8     @Override
 9     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10         System.out.println(method.getName() + "方法调用前");
11         Object result = method.invoke(obj, args);
12         System.out.println(method.getName() + "方法调用后");
13         return result;
14     }
15 }

  测试:

 1 public class Client {
 2     public static void main(String[] args) {
 3         IStudentService studentService = new StudentService();
 4 
 5         // 创建 InvocationHandler 实现
 6         InvocationHandler myInvocationHandler = new MyInvocationHandler(studentService);
 7 
 8         // 通过 Proxy.newProxyInstance 动态生成代理对象
 9         IStudentService studentServiceProxy = (IStudentService) Proxy.newProxyInstance(
10                 studentService.getClass().getClassLoader(),
11                 studentService.getClass().getInterfaces(),
12                 myInvocationHandler);
13         studentServiceProxy.insertStudent();
14         studentServiceProxy.deleteStudent();
15     }
16 
17 }

运行结果:

1 insertStudent方法调用前
2 添加学生
3 insertStudent方法调用后
4 deleteStudent方法调用前
5 删除学生
6 deleteStudent方法调用后

   2. CGLIB动态代理

  JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.

注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用。


   四、JDK和CGLib动态代理对比
  JDK 动态代理是实现了被代理对象所实现的接口,CGLib是继承了被代理对象。
  JDK和CGLib 都是在运行期生成字节码,JDK是直接写Class字节码,CGLib 使用 ASM 框架写Class字节码,Cglib代理实现更复杂,生成代理类的效率比JDK代理低。
JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass机制直接调用方法,CGLib 执行效率更高。

1. 原理区别:
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3)如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

2. 性能区别:
1)CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。

3. 各自局限:
1)JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。
2)cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
3)由于CGLib实现代理方法的方式是重写父类的方法,所以无法对final方法,或者private方法进行代理,因为子类无法重写这些方法;


五、静态代理和动态的本质区别

1. 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3. 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

参考链接:
https://nilzzzz.github.io
https://juejin.cn/post/7011357346018361375

 

标签:调用,JDK,静态,代理,接口,CGLIB,动态
From: https://www.cnblogs.com/hld123/p/18210054

相关文章

  • JavaScript 动态网页实例 —— 简单的表单验证
            表单验证是网页设计、特别是网站设计中必不可少的内容,狭义的验证指“用户是否进行了输入”、“输入的数据类型或格式是否符合要求”等,而广义的验证则包括口令确认与身份验证等内容。本章和下一章对表单验证的方式、方法进行介绍,本章只介绍一些简单的验证,包括:......
  • C语言:动态内存管理的学习1
    动态内存管理本章重点为什么存在动态内存分配?动态内存函数的介绍mallocfreecallocrealloc常见的动态内存错误·几个经典的笔试题·柔性数组一.为什么存在动态内存分配?我们已经学到的开辟空间的方法:inta;intarr[100];单个开辟空间或者连续开辟空间;在数组的......
  • 关于动态库
    windows下常用:1.静态链接lib库2.导入库.lib和动态链接库dll配合使用动态库和静态库内容查看vsstudio提供dumpbin.exe使用:1.打开vs命令行工具 2.输入指令 程序|指令|输出路径指令+相对路径(或者绝对路径,相对路径“./”表示当前路径,不要忘记写)|要进行导出的文件名同导出......
  • 算法设计与分析---动态规划(期末)
    1.基本要素 最优子结构-->一个问题的解包含子问题的最优解重叠子问题-->子问题被反复计算2.动态规划和分治区别两者都是把大问题转换成小问题/子问题来解决,并且当最优子问题组合成最优大问题。区别1:解决问题的类型动态规划主要用于解决优化问题,即寻找满足一定条件的最优解......
  • PCDN哪家收益最高?PCDN免费代理飞算云机顶盒路由器
    近年来,随着互联网的快速发展,网上赚钱已经成为许多人的选择。其中,PCDN(内容付费分发网络)平台因其独特的盈利模式备受关注。→→【点击此处,了解详情】←←但在众多PCDN平台中,到底哪家收益呢?接下来,我们将对几家PCDN平台进行深入分析,为您揭晓答案。一、平台A:用户规模庞大,......
  • 10-2-uptime查看系统负载-top动态管理进程
    10.2.1uptime查看CPU负载工具 任务队列的平均长度是什么?案例1:找出当前系统中,cpu负载过高的服务器。服务器1:loadaverage:0.15,0.08,0.011核服务器2:loadaverage:4.15,6.08,6.011核服务器3:loadaverage:10.15,10.08,10.01......
  • 买卖股票相关算法-动态规划-python
    要求1:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不......
  • Java静态变量在静态方法内部无法改变值
    一、如何解决“Java静态变量在静态方法内部无法改变值”的问题在Java中,静态变量(也称为类变量)属于类本身,而不是类的任何特定实例。它们可以在没有创建类的实例的情况下访问和修改。如果我们发现在静态方法内部无法改变静态变量的值,这通常是因为我们的代码中有一些逻辑错误或误解。......
  • nacos启动异常和动态配置未生效
    最近打算研究一下nacos动态配置的原理,于是开始照着Nacos官网快速入门。但是发现在和SpringBoot以及SpringCloud适配启动的时候,总是报错无法启动,于是深入排查了一下并作以记录。1.nacos和SpringBoot启动报错Invaliddefault:publicabstractcom.alibaba.nacos.api.config.Con......
  • 新版本的禅道在使用 nginx 做 https 反向代理时,无法正常安装、升级以及登录
    是想从docker的12.5.3版本升级到16.0版本,但是按步骤升级完成后,发现会卡在一个“禅道开源版15版本升级”的介绍页面无限循环直接部署全新的系统,也会卡在配置数据库连接的页面无限循环而且在容器的日志中,会有一条ERROR1045(28000):Accessdeniedforuser'root'@'loca......