当前位置:首页 > CN2资讯 > 正文内容

代理模型-静态代理And动态代理

2天前CN2资讯


前言

        在面向对象系统中,有些对象由于某些原因,比如对象创建开销很大,或者某些操作需要安全控制,直接访问会给使用者或者系统结构带来很多麻烦。为了解决这个场景的途径之一,便是增加一个中间层作为代理层。这边是代理模式。

        代理模式 ,作为23种经典设计模式之一在很多地方有用到,其目的是为其他对象提供一种代理以控制对这个对象的访问。通过代理可以为原对象附加多种用途。

        代理模式又分为动态代理和静态代理。而静态代理需要自己声明代理类对象。动态代理不需要声明代理类对象,通过程序自动生成。

        代理模式更多的是通过增加一个中间层作为代理层对访问代理对象做额外的控制,真正操作还是代理对象进行操作。对于装饰器模式而已,而是在原对象增加新的功能

        基于对代理模式应用场景的理解,我想到一个场景,找董事长签字的时候,一般是找秘书。先由秘书检查完文件,秘书再将文件拿给董事长签字。在这个场景下,秘书便是代理层,代理对象是董事长,对董事长签文件这个过程进行了控制(即检查文件)。

静态代理

秘书和董事长签字这个场景,用静态代理怎么写呢?其结构如下:

        需要有个基础类(People),用来声明代理层和代理对象共同的方法,代理对象继承基础类,并真正实现具体方法。代理层同样继承基础类,来实现相应的方法,但与代理对象不同的是,代理层可以在实现方法时加额外的能力,如校验功能。然后通过调用代理对象来实现真正的操作。

在写代码的时候,根据基础类(People) --> 代理对象(Boss) --> 代理层(Secretary)顺序编写。

基础类

首先声明一个基础类--People,里面只有一个方法声明,签字sign。代理对象和代理层基层基础接口,从而对外面展示是一样的动作。

public interface People { void sign(String file); }

代理对象

然后写代理对象,也就是真正签字的人 - 董事长(Boss)。董事长继承基础类People

public class Boss implements People { @Override public void sign(String file) { System.out.println("我是董事长,我来签字,文件为:"+ file); } }

代理层

然后写代理层 -- 秘书(Secretary)。秘书也继承基础类People,从而和董事长有一样的方法,但秘书要多一步检查文件(checkContract)的操作。

public class Secretary implements People { private People boss; public Secretary(People p){ this.boss = p; } @Override public void sign(String file) { System.out.println("我是秘书,签字的请找我,请您在外稍等一下"); if (checkContract(file)){ boss.sign(file); }else { System.out.println("我是秘书,文件不合格,请回"); } } public boolean checkContract(String file){ System.out.println("我是秘书,我检查文件"); if ("合同".equals(file)){ return true; } return false; } }

调用方法

public static void main(String[] args) { Secretary secret = new Secretary(new Boss()); secret.sign("合同"); secret.sign("协议"); }

调用结果如下。可见虽然调用的是代理层 -- 秘书(Secretary)的签字方法,但真实签字的还是代理对象 - 董事长(Boss)。但代理层提供一种代理以控制对这个对象的访问。当然通过代理还可以为原对象附加其他功能。

动态代理

        秘书除了可以代理董事长,也可以代理相关部门进行改签盖章,若要代理部门的时候,显然现在的写法是行不通的。

        上面的例子中代理类代理层 -- 秘书(Secretary)实现了抽象角色的接口(People),并在属性中声明了被代理的对象(Boss,也是Peple的实现类),导致代理类受到限制无法通用。为了让代理类更具有通用性,只需要使用反射动态的获取抽象接口的类型,进而获取相关方法实现动态代理。

        若想实现秘书除了可以代理董事长,也可以代理相关部门进行改签盖章这个功能,则需要用到动态代理,使用到了反射。

Jdk代理

第一个方案,Jdk代理,即使用Java原生反射机制进行动态代理。

        首先除了上面写的代理基础类People和代理对象Boss。现在再加一种要代理的对象-- 部门

新的代理基础类--Organization(部门)

public interface Organization { void sign(String file); }

新的代理对象--LegalDepartment(法务部)

public class LegalDepartment implements Organization{ @Override public void sign(String file) { System.out.println("我是法务部,我来签字,文件为:"+ file); } }

        这个时候代理层怎么写呢?答案:实现 InvocationHandler 接口,表明该类是一个动态代理执行类。

其重点有两个

1. InvocationHandler 接口内有一实现方法如下: public Object invoke(Object proxy, Method method, Object[] args) 。使用时需要重写这个方法


2.获取代理类,需要使用 Proxy.newProxyInstance(Clas loader, Class[] interfaces, InvocationHandler h) 这个方法去获取Proxy对象(Proxy 类类型的实例)。

代码如下:

public class Secretary implements InvocationHandler { private Object proxyTarget; public Object getProxyInstance(Object target) { this.proxyTarget = target; // 第一个参数,是类的加载器 // 第二个参数是代理对象的接口类型, // 第三个参数就是代理对象类本身 return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this); } /** * * @param proxy 调用该方法的代理对象 * * @param method 在代理对象上调用的接口方法 * * @param args 在代理实例上的方法调用中传递的参数 * * @return 代理对象某个方法的执行结果 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object methodObject = null; System.out.println("我是秘书,签字的请找我,请您在外稍等一下"); if (checkContract((String) args[0])){ methodObject = method.invoke(proxyTarget, args); }else { System.out.println("我是秘书,文件不合格,请回"); } return methodObject; } public boolean checkContract(String file){ System.out.println("我是秘书,我检查文件"); if ("合同".equals(file)){ return true; } return false; } }

调用方法

public static void main(String[] args) { Secretary secret = new Secretary(); // 继承父类方法 Pepple boss = (Pepple) secret.getProxyInstance(new Boss()); boss.sign("合同"); Organization legalDepartment = (Organization) secret.getProxyInstance(new LegalDepartment()); legalDepartment.sign("协议"); }

调用结果,可见现在代理层可以实现了通用。

CgLib代理

        第二个方案,CGLib代理,即要引入cglib.jar进行使用,从而实现动态代理。与Jdk代理不同的是,Jdk代理对象需要继承基础类,而CGLib代理可以直接使用子类,不需要基础类,但在使用的时候直接使用子类,在实现。此外,Jdk代理只用代理Public方法,CGLib可以代理protected方法。

代理层的修改        

        代理层继承MethodInterceptor,在getProxyInstance方法中实现代理对象的方法改为了Enhancer.create();重新方法改为了intercept();

public class Secretary implements MethodInterceptor { private Object proxyTarget; public Object getProxyInstance(Object target) { this.proxyTarget = target; return Enhancer.create(target.getClass(), target.getClass().getInterfaces(), this); } /** * * @param o 调用该方法的代理对象 * * @param method 在代理对象上调用的接口方法 * * @param objects 在代理实例上的方法调用中传递的参数 * * @methodProxy 它的作用是用于执行目标(委托类)的方法,, * 官方的解释是速度快且在intercept内调用委托类方法时不用保存委托对象引用。 * * @return 代理对象某个方法的执行结果 * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object methodObject = null; System.out.println("我是秘书,签字的请找我,请您在外稍等一下"); if (checkContract((String) objects[0])){ methodObject = method.invoke(proxyTarget, objects); }else { System.out.println("我是秘书,文件不合格,请回"); } return methodObject; } public boolean checkContract(String file){ System.out.println("我是秘书,我检查文件"); if ("合同".equals(file)){ return true; } return false; } }

调用方法

public static void main(String[] args) { Secretary secret = new Secretary(); // 可以直接继承子类 Boss boss = (Boss)secret.getProxyInstance(new Boss()); boss.sign("合同"); LegalDepartment legalDepartment = (LegalDepartment) secret.getProxyInstance(new LegalDepartment()); legalDepartment.sign("合同"); }

调用结果

  • CGLib (基于子类的动态代理)使用的是方法拦截器 MethodInterceptor ,需要导入 cglib.jar 和 asm.jar 包
  • 基于子类的动态代理,返回的是子类对象
  • 方法拦截器对 protected 修饰的方法可以进行调用
  • 总结

            通过代理模式 实现为其他对象提供一种代理以控制对这个对象的访问,从而为原对象附加多种用途。

            代理模式又分为动态代理和静态代理。而静态代理需要我们自己写代理类对象。动态代理不需要写代理类对象,通过程序自动生成。

            代理模式更多的是通过增加一个中间层作为代理层对访问代理对象做一下控制,真正操作还是代理对象进行操作。对于装饰器模式而已,而是在原对象增加新的功能



    静态代理

    某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

    动态代理

    代理角色不固定,更加灵活,代理对象由Java反射机制动态产生,无需程序员手动编写它的源代码。

            其中动态代理又分为Jdk代理和CgLib代理,其区别在于


    原理

    Jdk代理

  • 使用Java原生反射机制,代理层实现 InvocationHandler 接口
  • 由于Jdk代理对象需要继承基础类,需要代理对象需要实现基础类的接口
  • CgLib代理

  • CGLib代理可以直接使用子类,不需要基础类,但在使用的时候直接使用子类,
  • 参考资料

    装饰器:装饰器模式 | 菜鸟教程

    适配器:适配器模式 | 菜鸟教程

      你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/23877.html

      分享给朋友:

      “代理模型-静态代理And动态代理” 的相关文章

      eno VPS:掌握网络接口命名规则与性能优化技巧

      在了解eno VPS之前,我们先来看看什么是eno命名规则。ena作为一种网络接口命名方式,通过特定的规则来表示Linux系统中的网络设备。这种规则帮助用户更容易地识别和管理各种网络接口。具体来说,eno采用的是eno[n|d]的格式,主要用于板载设备。而对于热插拔设备,则使用ens[f][n|d]...

      LeaseWeb旧金山数据中心:为企业提供高效IT基础设施解决方案

      在谈到全球范围内的IT基础设施解决方案时,LeaseWeb无疑是一个重要的名字。成立于荷兰的LeaseWeb,凭借其卓越的服务和强大的网络能力,已经发展成为一家全球性的科技公司。它不仅提供传统的独立服务器服务,还涵盖了云计算、服务器托管等多样化的解决方案。对我而言,LeaseWeb就像是一座桥梁,连...

      RackNerd在纽约的数据中心服务与优势

      RackNerd在纽约的数据中心的服务确实引人关注。从基础服务到多样化的产品,RackNerd为各类客户提供了丰富的选择。我对于他们的虚拟主机和独立服务器十分赞赏。这些服务不仅安全可靠,而且具有灵活的配置选项,满足了各种不同需求的用户。对于初创企业或者需要特别高可用性的业务,选择RackNerd无疑...

      如何选择合适的IPv6 VPS服务商与其优势分析

      当我第一次接触到IPv6 VPS时,我意识到这一概念不仅为技术界带来了全新的视野,也为我们解决互联网地址匮乏的问题提供了有效的方案。IPv6,全称为互联网协议第六版,设计的目的是为了替代现有的IPv4协议,以应对未来互联网规模的增长。IPv6不仅增加了可用的地址数量,更在安全性和效率上进行了提升,是...

      如何解决IP被封的问题及预防措施

      在互联网的世界中,IP被封是一件不容小觑的事情。那么,为什么IP会被封呢?这其中有许多常见的原因值得我们关注。 首先,频繁请求是一个主要的原因。当我们频繁访问某个网站时,网站会检测到异常的请求频率,这通常会被视为一种攻击行为。想象一下,一个用户在短时间内不断刷新页面,网站为了保护自身的安全,可能会将...

      zgocloud:提升数据管理效率的创新云计算解决方案

      zgocloud的定义与背景 在当今数字化快速发展的时代,zgocloud作为一种创新的云计算解决方案逐渐崭露头角。简单来说,zgocloud是一种基于云技术的平台,可以提供灵活的存储和计算能力。它的设计旨在更好地满足不同企业和个人用户在数据处理和管理方面的需求。成立之初,zgocloud就定位于提...