二十四种设计模式(3)-代理模式

前言

代理模式是很多框架的底层应用,spring提供了很好的代理模式,一般不用开发者手动编写代理模式实现业务,但是原理很重要。代理分为静态代理和动态代理,根据实现方式又分为JDK动态代理和CGLIB动态代理,下面从两个维度分析了代理模式:

  1. 从源码角度分析了JDK动态代理和CGLIB动态代理的区别及适用场景;
  2. 静态代理和动态代理的区别,为什么静态代理使用比较少?

代理模式-JDK动态代理

代理模式分为两种:静态代理和动态代理。静态代理是在代码编译期,就给被代理对象生成了一个不可改变的代理类,代理少的时候可以用,效果比动态代理更好。动态代理就是在运行期间动态生成代理类,需要消耗的时间会更久一点,但是数量太多的时候使用更方便。
下面给出静态代理的例子。zhangsan给wangwu送花、邀请看电影,由lisi代替去做。这里zhangsan就是代理类,lisi就是被代理类。

  1. 创建业务接口
    在这里插入图片描述

  2. 创建业务接口的实现类,重写业务方法
    在这里插入图片描述

  3. 创建业务接口的代理实现类,重写业务方法。代理实现类中必须有被代理的实现类
    在这里插入图片描述

  4. 执行及结果
    在这里插入图片描述
    在这里插入图片描述

从上面的静态代理示例可看出两个问题:
首先,一般业务接口有很多,可以理解为我们写spring项目的service接口,需要为每个业务实现类都写一个静态代理,这样代理代码会很多。
其次,一个业务接口中,会有很多业务方法,代理类中需要重写每个业务方法,假如都是给业务方法加上事务,那就会有很多重复的代码。
为了解决上述问题,基于反射实现动态代理,动态代理只需要传入被代理类,就能够获取到代理对象,完成功能的增强,适用于所有实现了接口的类。代理类是在使用的时候获取的,也就是在使用的时候,通过传入的被代理类,通过实现被代理类的接口,实现代理。
下面实现事务的动态代理:
5. 业务接口
在这里插入图片描述

  1. 业务接口实现类
    在这里插入图片描述

  2. 新建类,实现InvocationHandler接口。动态代理没有明确的LiSi了,而是使用反射机制动态获取LiSi,也就是代理类需要动态获取。
    在这里插入图片描述

  3. 执行接结果
    在这里插入图片描述

在这里插入图片描述

看完动态代理的例子,下面探讨一下为什么JDK动态代理要求被代理类必须实现了某一接口,或者本身是接口?
这是因为动态代理生成的代理类继承了Proxy类,并且会实现被代理对象实现的所有接口,看一下Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);这句代码,在创建代理类对象的时候,传入了被代理类的所有接口target.getClass().getInterfaces(),代理类会去实现这些被传入的接口,从而重写所有接口的所有方法。由此可见,我们可以将生成的代理类强转为任意一个被代理类实现的接口或者Proxy去使用,但是Proxy里面几乎全是静态方法,没有实例方法,所以转换成Proxy意义不大,几乎没什么用。假设我们的被代理类没有实现任何接口,那么就意味着你只能将生成的代理类强转换成Proxy,那么就算生成了代理类,其实也没什么用。
网上给出了一个方法,可以将代理类的class文件保存到本地,然后使用反编译工具看到代理类的代码,下面是反编译之后的部分代码,其中VIPMovie是业务接口,GangTieXiaMovie实现了VIPMovie接口,动态代理Proxy0可以看到继承了Proxy接口,并且实现了VIPMovie业务接口。
在这里插入图片描述

代理模式-CGLIB动态代理

JDK动态代理要求被代理类必须实现了接口,如果被代理类没有实现接口,那么使用JDK动态代理就会出现问题,因为代理类已经继承了Proxy类,如果再继承被代理类,就会出现问题,因为java是单继承。这就出现了Cglib动态代理,但是被代理类需要被继承,因此被代理类不能是final修饰的类。示例如下:

  1. 创建业务类,不能被final修饰,不实现接口
    在这里插入图片描述

  2. 创建动态代理类,底层一样是反射机制实现
    在这里插入图片描述

  3. 结果及展示
    在这里插入图片描述在这里插入图片描述

可以看得出来,不管是JDK还是Cglib动态代理,都是利用了反射机制,虽然实现原理有点不同,但是代码结构很类似。网上给出了一个方法,可以将代理类的class文件保存到本地,然后使用反编译工具看到代理类的代码,下面是反编译之后的部分代码,其中CaptainAmerica2MovieImpl是业务类,动态代理CaptainAmerica2MovieImpl E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB5c3ddcfe继承了CaptainAmerica2MovieImpl接口,并且实现了Factory接口。
在这里插入图片描述

从代理对象反编译源码可以知道,代理对象继承于CaptainAmerica2MovieImpl ,拦截器调用intercept()方法, intercept()方法由自定义CglibProxyInterceptor实现,所以,最后调用CglibProxyInterceptor中的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值