news 2026/9/11 10:52:33

代理设计模式与Java动态代理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代理设计模式与Java动态代理

一、代理设计模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

意思就是不想直接暴露目标对象,而提供个拦截缓冲。日常开发中,会把对目标对象的请求都重定向于代理对象,代理对象会进行方法的加强,例如进行调用前后的一些操作,Spring AOP就是其中一个应用的案例。

上图就是代理模式的结构图,一般会设置一个接口类,目标类和代理类都实现同一套接口,代理类会持有目标类的实例。客户端调用代理类的方法,从而间接调用目标类的方法。为了方便,直接上代码:

1、接口类

2、目标类

3、代理类

package proxy; public class StaticProxyObjet implements Myinterface{ TargetObjet targetObjet; public StaticProxyObjet(TargetObjet targetObjet){ this.targetObjet=targetObjet; }; @Override public void doSomething() { beforInvoke("doSomething"); targetObjet.doSomething(); afterInvoke("doSomething"); } @Override public void somethingElse(String arg) { beforInvoke("somethingElse"); targetObjet.somethingElse(arg); afterInvoke("somethingElse"); } private void beforInvoke(String arg){ System.out.println("调用方法"+arg+"前"); }; private void afterInvoke(String arg){ System.out.println("调用方法"+arg+"后"); }; }

4、测试类

package proxy; public class StaticProxyTest { public static void main(String[] args) { StaticProxyObjet proxy = new StaticProxyObjet(new TargetObjet()); proxy.doSomething(); proxy.somethingElse("20220601"); } }

输出结果:

常规设计模式中的代理就是上述的静态代理,

其优点:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

有没办法,既保留代理的优点的同时,对其缺点解决呢?答案是肯定的,那就是动态代理,常见的动态代理有两种方式,jdk动态代理以及cglib动态代理。

二、jdk动态代理

与静态代理方式类似类似,都需要提供一个接口类,然后目标类实现该接口。不同的地方是不需要在直接编写代理类,而是编写实现InvocationHandler 接口的处理器。

对目标对象的调用请求都会被重定位到这个处理器上,而且由InvocationHandler这个接口的 invoke 方法来进行调用。

1、处理器类

留意,method.invoke(target,args)中首个参数,使用的并非方法this.invoke()里的proxy对象,而是构造器赋值的目标对象。

package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxyHandler implements InvocationHandler { public Object target; public DynamicProxyHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy:"+proxy.getClass()+" method:"+ method +" args:"+args); return method.invoke(target,args); } }

2、测试类

package proxy; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { Myinterface proxy = (Myinterface) Proxy.newProxyInstance(Myinterface.class.getClassLoader(),new Class[]{Myinterface.class},new DynamicProxyHandler(new TargetObjet())); proxy.doSomething(); proxy.somethingElse("2022年六一儿童节"); } }

输出结果:

jdk的优点就是不需要编写实现统一接口的代理类,动态代理类只会在运行期间存在于内存。不过对于没有实现接口的目标类来说,就不能使用这种方式。因为$Proxy0是已经继承了Proxy这个父类,根据jjava的单继承特性,它就不能再继承其他父类。

gclib就是针对没有实现接口的类实现动态代理。

三、CGLib实现动态代理

GCLib是对底层的asm字节码技术进行调用,可以对为一个类创建子类,该子类设置方法拦截器,对父类方法的调用都会被子类拦截并进行处理。具体实现流程如下:

1、编写目标类

package proxy; public class CglibTarget { public void say(){ System.out.println("hello CglibTarget!"); } }

2、编写拦截器

package proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibTargetProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ // 设置代理目标 enhancer.setSuperclass(clazz); // 设置单一回调对象,在调用中拦截对目标方法的调用 enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("事务启动..."); methodProxy.invokeSuper(o, objects); System.out.println("关闭事务"); return methodProxy; } }

该拦截器需要实现MethodInterceptor,并创建Enhancer增强器(为无接口的类创建代理)。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,生成的所有非final的方法都带有回调钩子。

3、测试类

package proxy; public class CglibProxyTest { public static void main(String[] args) { CglibTargetProxy proxy = new CglibTargetProxy(); CglibTarget target1 = (CglibTarget) proxy.getProxy(CglibTarget.class); target1.say(); } }

运行结果:

四、jdk动态代理和CGLib的比较

jdk动态代理
实现机制委托机制。目标类和代理类实现统一接口,提供的处理器实现InvocationHandler并持有目标类。代理类委托处理器来掉用目标类的方法。
回调方式反射
优点

无需依赖第三方jar包

缺点

1、只能代理实现接口的类

2、速度较慢

适用场景目标对象实现了接口
效率瓶颈反射调用的速度稍慢
CGLib
实现机制继承机制。代理类采用方法拦截的技术拦截所有父类方法的调用并顺势志入横切逻辑。
回调方式通过FastClass方法索引调用
优点

1、代理对象执行速度较快

2、可代理没有实现接口的类

缺点

1、不能代理final类

2、生成字节码较慢

适用场景

1、非接口类

2、非final类

3、不包含final方法

效率瓶颈首次调用,需要生存多个子类的Class对象比jdk方式要慢,多次调用则速度优势更明显。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 10:52:25

AI短剧生产工作流:从剧本结构到成片审片的6步实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:50:24

初探Netty:Netty原理、核心组件、数据容器以及运行机制

Netty 是一个异步事件驱动的通信框架,可用于搭建高性能协议服务器和客户端。 一、NIO Selector机制是NIO的核心: 当客户端请求时,就创建一个scoketChannel,并注册到Selector上(多路复用器)Selector关注服…

作者头像 李华
网站建设 2026/9/11 10:48:54

发票识别工具实战:OCR、字段提取与校验全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:48:01

Origin科研绘图导出空白边距问题解决方案

1. 问题背景与核心痛点做科研绘图的朋友们肯定都遇到过这样的场景:在Origin里精心调整好的图表,导出为TIF/PNG/JPG格式后,打开一看发现四周莫名其妙多出一圈空白边距。这个问题看似简单,实则困扰着大量科研工作者——我实验室的师…

作者头像 李华
网站建设 2026/9/11 10:47:42

老程序猿-Eclipse模式在IDEA中的快捷键总结

Eclipse模式在IDEA中的快捷键总结 1.CtrlD: 删除当前行 2.CtrlAlt↓: 复制当前行到下一行(复制增加) 3.CtrlAlt↑: 复制当前行到上一行(复制增加) 4.Alt↓: 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了) 5.Alt←: 前一个编辑的页面 6.Alt→: 下一个编辑的页面 …

作者头像 李华
网站建设 2026/9/11 10:47:34

Python实现SOM神经网络:自组织映射的高维数据可视化与聚类实战

简介:这是一个基于Python实现的自组织映射神经网络项目,面向机器学习初学者以及需要数据聚类、降维或可视化分析的开发者。自组织映射通过无监督竞争学习将高维输入映射到低维网格,该项目从零展示核心流程:初始化权重、计算最佳匹…

作者头像 李华