作者:爱宝贝丶来源:https://my.oschina.net/zhangxufeng/blog/1633187

代理模式最典型的应用就是AOP,本文结合主要讲解了代理模式的几种实现方式:静态代理和动态代理,这里动态代理又可以分为jdk代理和Cglib代理,另外,本文也对这几种代理模式的优缺点进行了对比。

代理,顾名思义,即代替被请求者来处理相关事务。代理对象一般会全权代理被请求者的全部只能,客户访问代理对象就像在访问被请求者一样,虽然代理对象最终还是可能会访问被请求者,但是其可以在请求之前或者请求之后进行一些额外的工作,或者说客户的请求不合法,直接拒绝客户的请求。如下图所示为代理模式的一份简图:

代理模式的角色:

  • ISubject:代理者与被代理者共同实现的接口,可以理解为需要代理的行为;
  • SubjectImpl:被代理者,其为具有某种特定行为的实现者;
  • SubjectProxy:代理者,其会全权代理SubjectImpl所具有的功能,在实现其功能的基础上做一些额外的工作;
  • Client:客户端,客户端访问代理者与访问被代理者具有类似的效果,其无法区分访问的是代理者还是被代理者。

1. 静态代理

静态代理模式也即上图中描述的这种模式,从图中可以看出,SubjectProxy保存一个ISubject实例,当客户端调用SubjectProxy的request()方法时,其除了做额外的工作之外,还会调用ISubject实例的request()方法。如下是这三个类的一个简单实现:

可以看到,代理对象在调用被代理对象的方法之前和之后都打印了相关的语句。如下是客户端请求示例:

public class Client { @Test public void testStaticProxy() { ISubject subject = new SubjectImpl(); ISubject proxy = new SubjectProxy(subject); proxy.request(); }}

运行上述用例,可得到如下结果:

before safety check.request SubjectImpl.after safety check.

从客户端访问方式可以看出,客户端获取的是一个实现ISubject接口的实例,其在调用的request()方法实际上是代理对象的request()方法。这种代理方式称为静态代理,并且这种代理方式也是效率最高的一种方式,因为所有的类都是已经编写完成的,客户端只需要取得代理对象并且执行即可。

静态代理虽然效率较高,但其也有不可避免的缺陷。可以看到,客户端在调用代理对象时,使用的是代理对象和被代理对象都实现的一个接口,我们可以将该接口理解为定义了某一种业务需求的实现规范。如果有另外一份业务需求(如进行数据修改),其与当前需求并行的,没有交集的,但是其在进行正常业务之外所做的安全验证工作与当前需求是一致的。如下是我们进行该数据修改业务的实现代码:

如下是客户端代码:

public class Client { @Test public void testStaticProxy() { ISubject subject = new SubjectImpl(); ISubject proxy = new SubjectProxy(subject); proxy.request();  IUpdatable updatable = new UpdatableImpl(); IUpdatable proxy = new UpdatableProxy(updatable); proxy.update(); }}

可以看到,要实现相同的对象代理功能(安全验证),静态代理方式需要为每个接口实现一个代理类,而这些代理类中的代码几乎是一致的。这在大型系统中将会产生很大的维护问题。

2. 动态代理

① jdk代理

所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可,如下是实现该接口的一个示例:

如下是客户端调用方式:

public class Client { @Test public void testDynamicProxy() { ISubject subject = new SubjectImpl(); ISubject proxySubject = (ISubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{ISubject.class}, new SafetyInvocationHandler(subject)); proxySubject.request(); IUpdatable updatable = new UpdatableImpl(); IUpdatable proxyUpdatable = (IUpdatable) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IUpdatable.class}, new SafetyInvocationHandler(updatable)); proxyUpdatable.update(); }}

可以看到,客户端在调用代理对象时使用的都是同一个SafetyInvocationHandler。这里jdk代理其实在底层利用反射为每个需要代理的对象都创建了一个InvocationHandler实例,在调用目标对象时,其首先会调用代理对象,然后在代理对象的逻辑中请求目标对象。这也就是为什么在代理类中可以保存目标对象实例的原因,比如上述的SafetyInvocationHandler,其声明了一个Object类型的属性用来保存目标对象的实例。

jdk代理解决了静态代理需要为每个业务接口创建一个代理类的问题,虽然使用反射创建代理对象效率比静态代理稍低,但其在现代高速jvm中也是可以接受的,在Spring的AOP代理中默认就是使用的jdk代理实现的。这里jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理。

② Cglib代理

Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象实现某个接口的问题。对于需要代理的类,如果能为其创建一个子类,并且在子类中编写相关的代理逻辑,因为“子类 instanceof 父类”,因而在进行调用时直接调用子类对象的实例,也可以达到代理的效果。Cglib代理的原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行。这也就是Cglib代理为什么不需要为每个被代理类编写代理逻辑的原因。这里需要注意的是,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写。如下是使用Cglib代理的一个示例:

/** * 被代理类 */public class Suject { public void request() { System.out.println("update without implement any interface."); }}/** * 代理类 */public class SafetyCheckCallback implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before safety check."); Object result = methodProxy.invokeSuper(o, objects); System.out.println("after safety check."); return result; }}

如下是客户端访问方式:

public class Client { @Test public void testCglibProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Suject.class); enhancer.setCallback(new SafetyCheckCallback()); Suject proxy = (Suject) enhancer.create(); proxy.request(); }}

可以看到,客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中。

3. 总结

本文主要对代理模式的三种实现方式进行了详细讲解,并且比较了各个代理方式的优缺点,Spring主要使用的是动态代理方式实现切面编程的。这里读者可能会有一个疑问,即上述代理代码中,根据实现方式的不同,对客户端代码都有一定的侵入性,比如静态代理客户端需要侵入代理类的实例,jdk代理需要侵入Proxy类,而Cglib代理则需要侵入子类子类对象创建等代码。理论上,客户端只需要获取目标对象,无论是否为代理过的,然后调用其相关方法实现特定功能即可。这其实也是工厂方法的强大之处,因为工厂方法会将对象的创建封装起来,对象的具体创建过程可以根据具体的业务处理即可,客户端只需要依赖工厂类调用相关的方法即可。同样的这也就说明了Spring IoC容器是天然支持AOP代理的原因,因为其将对象的创建过程交由容器进行了。

利用代码分别实现jdk动态代理和cglib动态代理_代理模式实现方式及优缺点对比...相关推荐

  1. 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理

    大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...

  2. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  3. 【Spring6】| GoF之代理模式(JDK动态代理和CGLIB动态代理)

    目录 一:GoF之代理模式 1. 对代理模式的理解 2. 静态代理 3. 动态代理 3.1 JDK动态代理 3.2 CGLIB动态代理 一:GoF之代理模式 1. 对代理模式的理解 生活场景1:牛村的 ...

  4. 什么是代理模式?代理模式有什么用?通过一个小程序分析静态代理和动态代理。自己简单实现动态代理。JDK动态代理和CGLIB动态代理的区别。

    1. 代理模式有什么用 ①功能增强,在实现目标功能的基础上,又增加了额外功能.就像生活中的中介一样,他跟两边客户会有私下的交流. ②控制访问,代理不让用户直接和目标接触.就像中间商一样,他们不会让我们 ...

  5. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  6. JAVA 进阶篇 动态代理 JDK动态代理和CGlib动态代理

    JDK动态代理和CGlib动态代理 JDK动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. CGlib动态代理: 利用ASM(开源的Java ...

  7. Java两种动态代理JDK动态代理和CGLIB动态代理

    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...

  8. Spring AOP之---基于JDK动态代理和CGLib动态代理的AOP实现

    AOP(面向切面编程)是OOP的有益补充,它只适合那些具有横切逻辑的应用场合,如性能监测,访问控制,事物管理,日志记录等.至于怎么理解横切逻辑,敲完实例代码也就明白了. 为什么要使用AOP,举个栗子: ...

  9. Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

    视频功能审核通过了,可以看视频啦!记得点关注啊~ 注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了 记得点关注啊,视频里的wx二维码失效了,wx搜索:"聊5毛钱的java&q ...

  10. jdk动态代理和cglib动态代理实现及区别

    代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 代理模式又分为:静态代理.jdk动态代 ...

最新文章

  1. [转]搞ACM的你伤不起(转自Roba大神)
  2. python科学计算基础教程pdf下载-用Python做科学计算 pdf版
  3. 如何使WINCE启动时自动运行应用程序
  4. Unix toolbox注解2之Linux系统状态用户和限制
  5. Geoserver怎样设置地图shp文件为相对路径,可轻松复制移植
  6. 文明重启服务器维护怎么卡进去,文明重启更新之后进不去,具体玩法介绍
  7. java class类型参数_java – 如何从通用类型参数获取`.class`属性?
  8. C# 字符串按设置的格试在前面或后面增加空格或其它字符
  9. Inno_Setup使用笔记(简单完成安装包制作)
  10. uni-app请求图片的路径是反斜杠如何解决
  11. 【数据结构笔记07】不带头结点链表实现多项式相加、相乘
  12. 正态分布的前世今生:正态分布的进一步发展
  13. java 项目启动后页面乱码_java生成的Html打开后展示乱码
  14. 阿里mysql集群_MySQL集群搭建详解
  15. 标书制作详细教程(零基础速成,助力公司中标)
  16. RocketMQ源码分析(十五)之文件恢复
  17. 百度地图3d效果和卫星图效果
  18. 产业智能化的大江大河,需要AI安全这样守护
  19. CST结果导入MATLAB,请教大家可以将CST结果调入到MATLAB中进行计算吗
  20. [渝粤教育] 兰州文理学院 信息技术基础 参考 资料

热门文章

  1. GTK+开发环境搭建(Centos+Netbeans)
  2. 使用Html.DropDownList
  3. OSPF单域实验报告
  4. Linux常用备份恢复工具
  5. python爬虫ssl错误_Python爬虫:Requests的SSLError:certificate verify failed问题解决方案6条...
  6. python requests库详解_python的requests库详解
  7. [转载] java给对象中的包装类设置默认值
  8. bca ac如何联合索引_BCA的完整形式是什么?
  9. java后台json传递,后台json传递
  10. 怎么查看我的php版本,怎样查看php版本