代理模式

沙海 2021年6月29日01:03:16Java评论28字数 8014阅读26分42秒阅读模式
摘要

智能摘要

智能摘要文章源自JAVA秀-https://www.javaxiu.com/35741.html

这次要跟大家分享的是设计模式中三大类创建型中的代理模式,代理模式在业务场景上我们可能不会经常用到,但是面试官却会经常问一个问题?从图中可以看出其实整个接口还是很简单,就是一个真实对象以及代理对象。Enhancer对象把代理对象设置为被代理类的子类来实现动态代理的。看完上面的动态代理,不知道大家有没有想法,实现一个多级动态代理。同样的如果要实现三级代理,四级代理也就不是什么难事了,在每一层的上面再加一个代理对象就可以了。文章源自JAVA秀-https://www.javaxiu.com/35741.html

原文约 3089 | 图片 2 | 建议阅读 7 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/35741.html

代理模式

原创 三太子敖丙 三太子敖丙 文章源自JAVA秀-https://www.javaxiu.com/35741.html

收录于话题文章源自JAVA秀-https://www.javaxiu.com/35741.html

#设计模式文章源自JAVA秀-https://www.javaxiu.com/35741.html

11个文章源自JAVA秀-https://www.javaxiu.com/35741.html

设计模式已经跟大家分享很多了常见的模式了,感兴趣的小伙伴可以再回顾一下,巩固一下理解。文章源自JAVA秀-https://www.javaxiu.com/35741.html

这次要跟大家分享的是设计模式中三大类创建型中的代理模式,代理模式在业务场景上我们可能不会经常用到,但是面试官却会经常问一个问题?文章源自JAVA秀-https://www.javaxiu.com/35741.html

请你跟我讲讲Spring里面AOP的代理模式?jdk的代理模式和cglib的代理模式又啥区别?文章源自JAVA秀-https://www.javaxiu.com/35741.html

清楚和不清楚的同学都可以接着向下看,一定会有收获。文章源自JAVA秀-https://www.javaxiu.com/35741.html

言归正传,接下来开始一步一步分析一下代理模式。文章源自JAVA秀-https://www.javaxiu.com/35741.html

代理模式文章源自JAVA秀-https://www.javaxiu.com/35741.html

定义以及目的

首先代理模式可以分为多种类型文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 远程代理:就是将工作委托给远程对象(不同的服务器,或者不同的进程)来完成。常见的是用在web Service中。还有就是我们的RPC调用也可以理解为一种远程代理。文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 保护代理:该模式主要进行安全/权限检查。(接触很少)文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 缓存代理:这个很好理解,就是通过存储来加速调用,比如Sping中的@Cacheable方法,缓存特定的参数获取到的结果,当下次相同参数调用该方法,直接从缓存中返回数据。文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 虚拟代理:这种代理主要是为方法增加功能,比如记录一些性能指标等,或进行延迟初始化文章源自JAVA秀-https://www.javaxiu.com/35741.html

上面只是我们作为了解的概念,接下来再看看代理模式有哪些部分构成。文章源自JAVA秀-https://www.javaxiu.com/35741.html

代理模式文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • Subject(共同接口):客户端使用的现有接口文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • RealSubject(真实对象):真实对象的类文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • ProxySubject(代理对象):代理类文章源自JAVA秀-https://www.javaxiu.com/35741.html

从图中可以看出其实整个接口还是很简单,就是一个真实对象以及代理对象。文章源自JAVA秀-https://www.javaxiu.com/35741.html

目的:提供一个实际代理对象,以便更好的控制实际对象。以上定义来自《设计模式之美》文章源自JAVA秀-https://www.javaxiu.com/35741.html

代码举例实现

为了方便理解,还是举一个例子,不知道大家在读初中或者高中是否经历过传小纸条的过程,假如现在同学A 对同学C有一些话想聊(比如放学相约一起打游戏)但是因为现在是上课时间,又不能大声说,同学A和同学C之间坐了一个同学B,所以现在同学A只能是先找到同学B把纸条给它,让他转告同学C,但是去玩还是不是不去玩,那还是只能真正的同学C自己才能决定。文章源自JAVA秀-https://www.javaxiu.com/35741.html

所以代理模式可以理解为 同学B是同学的C的代理,同学A要找同学C,只能找到同学B,通过同学B转达同学C,同时将同学的C的执行结果反馈给同学A。文章源自JAVA秀-https://www.javaxiu.com/35741.html

说完了例子还是具体看看代码的实现吧文章源自JAVA秀-https://www.javaxiu.com/35741.html

public interface Subject {   // 共同的接口    void doSomething();}

定义一个共同的接口(即大家要做的事请:放学一起打游戏)文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class RealSubject implements Subject {   // 真实对象    @Override    public void doSomething() {        System.out.println("放学去打游戏");    }}

构建一个真实对象,即例子中的同学C文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class ProxySubject implements Subject {    private RealSubject realSubject;    public ProxySubject(RealSubject realSubject) {        this.realSubject = realSubject;    }    public ProxySubject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {        this.realSubject = (RealSubject) this.getClass().getClassLoader().loadClass("com.ao.bing.demo.proxyPattern.RealSubject").newInstance();    }    @Override    public void doSomething() {        realSubject.doSomething();    }    public static void main(String[] args) {        try {            // 第一种方式            new ProxySubject().doSomething();            // 打印结果: 放学去打游戏        } catch (Exception e) {            // 异常情况,代理失败,            // 传纸条的被老师抓了。或者同学C不在座位上了 等异常情况        }        // 第二种方式        new ProxySubject(new RealSubject()).doSomething();        // 打印结果: 放学去打游戏    }}

构建代理对象,即同学B,那么可以看到同学A并没有真实接触到同学C,通过同学B对同学C的代理就能知道同学C放学能不能跟他一起去打游戏文章源自JAVA秀-https://www.javaxiu.com/35741.html

在Main方法里面,有两种方式来调用真实对象文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 第一种:采用类加载器形式,去加载实列对象,这样我们就不同关心到底什么时候需要真实的实列化对象文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 第二种:通过传值的形式,把实列化对象传过来。(理解为装饰器模式了)文章源自JAVA秀-https://www.javaxiu.com/35741.html

    这里大家要区别一下,代理模式是提供完全相同的接口,而装饰器模式是为了增强接口。文章源自JAVA秀-https://www.javaxiu.com/35741.html

静态代理、动态代理和cglib代理分析

静态代理

在上面的举的列子实现其实就是静态代理,大家可以看到整体也比较简单。但是它的缺点也很明显文章源自JAVA秀-https://www.javaxiu.com/35741.html

静态代理需要为每一个对象都创建一个代理类,增加了维护成本以及开发成本,那么为了解决这个问题,动态代理就出来了,不要再固定为每一个需要代理的类而创建一个代理类文章源自JAVA秀-https://www.javaxiu.com/35741.html

动态代理

动态代理合理的避免了静态代理的那种方式,不用事先为要代理的类而构建好代理类。而是在运行时通过反射机制创建。文章源自JAVA秀-https://www.javaxiu.com/35741.html

在写动态代理事需要理解两个东西:Proxy 可以理解为就是调度器,InvocationHandler  增强服务接口可以理解为代理器。所以我个人理解动态代理其实就是一种行为的监听。文章源自JAVA秀-https://www.javaxiu.com/35741.html

具体的代码实现举一个例子:螳螂捕蝉,通过通过螳螂监听到蝉的动作。方便后面讲到多级代理模式。文章源自JAVA秀-https://www.javaxiu.com/35741.html

public interface BaseService {    void mainService();}public class Cicada implements BaseService {    @Override    public void mainService() {        System.out.println("主要业务,以蝉为例,当蝉出现业务调用时,螳螂监听到");    }}

创建共同接口,以及真实对象蝉文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class PrayingMantis implements InvocationHandler {    private BaseService baseService;  // 这里采用的是构建传参数,可以用反射,举的第一个例子有样式代码    public PrayingMantis(BaseService baseService) {        this.baseService = baseService;    }    // 螳螂主要业务,也就是监听对象    @Override    public Object invoke(Object listener, Method method, Object[] args) throws Throwable {        method.invoke(baseService,args);        secondaryMain();        return null;    }    // 这里理解增强业务,即我们可以在实现InvocationHandler里面添加其他的业务,比如日志等等。    private void secondaryMain(){        System.out.println("螳螂捕蝉 - 次要业务");    }}

创建螳螂类,监听着蝉的类的动作文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class BeanFactory {    public static BaseService newInstanc(Class classFile) {        // 1. 创建蝉,真实类对象        BaseService trueCicada = new Cicada();        // 2.创建代理类 螳螂        InvocationHandler prayingMantis = new PrayingMantis(trueCicada);        // 3.向Jvm索要代理对象 其实就是监听的对象,        Class classArray[] = {BaseService.class};        BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);        return baseService;    }     // 测试Demo    public static void main(String[] args) {        BaseService baseService  = newInstanc(Cicada.class);        baseService.mainService();        // 测试结果 :主要业务        //           螳螂捕蝉 - 次要业务    }}

通过结果可以看出当蝉主要业务发生调用时,螳螂能监听到蝉的业务并且能处理其他业务逻辑,这也就是Spring里面AOP为什么能处理日志切面等。文章源自JAVA秀-https://www.javaxiu.com/35741.html

代理的本质:文章源自JAVA秀-https://www.javaxiu.com/35741.html

我认为其实就是一种行为的监听,对代理对象($proxy InvocationHandler)的一种监听行为。文章源自JAVA秀-https://www.javaxiu.com/35741.html

代理模式组成:文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 接口:声明需要被监听行为文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 代理实现类(InvocationHandler):次要业务,次要业务和主要业务绑定执行文章源自JAVA秀-https://www.javaxiu.com/35741.html

  • 代理对象(监听对象)文章源自JAVA秀-https://www.javaxiu.com/35741.html

Cglib动态代理

cglib动态代理其实和jdk的动态代理是很相似的,都是要去实现代理器接口完成。文章源自JAVA秀-https://www.javaxiu.com/35741.html

具体代码如下:文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class PrayingMantis implements MethodInterceptor {    private Cicada cicada;// 代理对象    public Cicada getInstance(Cicada cicada) {        this.cicada = cicada;        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(this.cicada.getClass());        enhancer.setCallback(this);        return (Cicada) enhancer.create();    }    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        Object object = methodProxy.invokeSuper(o, objects);        secondaryMain();        return object;    }    private void secondaryMain() {        System.out.println("螳螂捕蝉 - 次要业务");    }    public static void main(String[] args) {        PrayingMantis prayingMantis = new PrayingMantis();        Cicada instance = prayingMantis.getInstance(new Cicada());        instance.mainService();        // 结果:主要业务        //      螳螂捕蝉 - 次要业务    }

因为蝉类都是一样的所以我就不单独这里再贴出来。文章源自JAVA秀-https://www.javaxiu.com/35741.html

细心的同学已经发现,Cglib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。文章源自JAVA秀-https://www.javaxiu.com/35741.html

Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因为是采用继承方式,所以代理类不能加final修饰,否则会报错。文章源自JAVA秀-https://www.javaxiu.com/35741.html

final类:类不能被继承,内部的方法和变量都变成final类型文章源自JAVA秀-https://www.javaxiu.com/35741.html

JDK和Cglib的区别:文章源自JAVA秀-https://www.javaxiu.com/35741.html

jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理文章源自JAVA秀-https://www.javaxiu.com/35741.html

cglib动态代理是利用ASM开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理文章源自JAVA秀-https://www.javaxiu.com/35741.html

ASM: 一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。            -- 以上ASM解释来自简书文章源自JAVA秀-https://www.javaxiu.com/35741.html

多级动态代理

看完上面的动态代理,不知道大家有没有想法,实现一个多级动态代理。文章源自JAVA秀-https://www.javaxiu.com/35741.html

还是以螳螂捕蝉为例子,再加上一个黄雀在后,实现多级动态代理模式。文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class Cardinal implements InvocationHandler {  // 监听代理代理对象    private Object proxyOne;    public Cardinal(Object proxyOne) {        this.proxyOne = proxyOne;    }    // 螳螂主要业务,也就是监听对象    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws            Throwable {        method.invoke(proxyOne, args);        secondaryMain();        return null;    }    private void secondaryMain() {        System.out.println("黄雀吃螳螂 - 次要业务");    }}

创建一个黄雀代理对象,那作为他的真实对象就变成螳螂了,当螳螂对象发生调用时,黄雀就能坚挺到,同时作出对应业务逻辑文章源自JAVA秀-https://www.javaxiu.com/35741.html

public class BeanFactory {    public static BaseService newInstanc(Class classFile) {        // 1. 创建蝉,真实类对象        BaseService trueCicada = new Cicada();        // 2.创建代理类 螳螂        InvocationHandler prayingMantis = new PrayingMantis(trueCicada);        // 3.向Jvm索要代理对象 其实就是坚挺的对象        Class classArray[] = {BaseService.class};        BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);        // 4.创建代理实现类 黄雀 二级代理        InvocationHandler cardinal = new Cardinal(baseService);        BaseService secondBaseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, cardinal);               // 假设要实现三级,四级代理,则在黄雀类上再加一层代理即可实现。       // 省略其他的更多级代理对象        return secondBaseService;    }       // 测试demo      public static void main(String[] args) {        BaseService baseService  = BeanFactory.newInstanc(Cicada.class);        baseService.mainService();        // 结果:主要业务        //      螳螂捕蝉 - 次要业务        //      黄雀吃螳螂 - 次要业务    }}

根据这个代码基本就实现多级代理过程。螳螂监听着蝉类的动作,黄雀监听着螳螂类的动作。文章源自JAVA秀-https://www.javaxiu.com/35741.html

同样的如果要实现三级代理,四级代理也就不是什么难事了,在每一层的上面再加一个代理对象就可以了。文章源自JAVA秀-https://www.javaxiu.com/35741.html

动态代理本质还是可以理解为将“次要业务”与“主要业务”解耦合,让开发者能更加专注于主要业务,提升开发效率,以及维护成本。文章源自JAVA秀-https://www.javaxiu.com/35741.html

总结

代理模式在业务代码上我个人认为是比较少见的,特别是动态代理基本上是没有见过。但是代理模式也是我们必须要理解的一种模式,因为学习好代理模式有助于我们去读一些源码,排查一些更深层次的问题,或者面对一些业务场景问题,也能有一个很大的提升,设计模式本身也就是为了解决问题而创建出来的。文章源自JAVA秀-https://www.javaxiu.com/35741.html

理解完动态代理现在对我们来说AOP的实现原理也就不言而喻了。文章源自JAVA秀-https://www.javaxiu.com/35741.html

详细的设计模式到这里就结束了,后面针对一些不常见设计模式我还是会给大家做一个总结吧。文章源自JAVA秀-https://www.javaxiu.com/35741.html

我是敖丙,你知道的越多,你不知道的越多,我们下期见!!!文章源自JAVA秀-https://www.javaxiu.com/35741.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
  • 版权声明:本站是JAVA秀团队的技术分享社区,我们会经常分享资源和教程。
  • 转载请注明:代理模式 - JAVA秀 ☜(ˆ▽ˆ)
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定