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

代理模式详解产品代理模式

2天前CN2资讯


什么叫代理模式?

代理模式是在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类增加功能。代理对象在在原始类和代理类之间起到了中介作用。

代理模式的目的:

  • 保护原始类;
  • 增强原始类;

从设计原则来说是开闭原则,对修改关闭,对扩展开放。

我们在生活中的经常遇到这样的场景如:房屋中介、猎头等。

中介保护了我们的隐私,我们除了提供基本的房屋买卖、找工作等需求,中介会通过自己的资源、渠道等帮我们实现,我们本来的意愿并没有改变。

静态代理

了解下yxkong要通过贝壳去买房的过程。

买房的流程

/** * 买房要求 * @author yxkong * @create 2021/5/13 * @since 1.0.0 */ public interface BuyHouse { /** * 买房要求 * @param totalPrice 总价 */ public void buy(double totalPrice); }

yxkong买房实现,目标类(原始类)

public class YxkongBuyHouse implements BuyHouse { @Override public void buy(double totalPrice) { System.out.println(String.format("yxkong买了100平,三室总价为%s的房子",totalPrice)); } }

yxkong通过中介ke买房子的代理实现

public class KeBuyHouseProxy implements BuyHouse { private BuyHouse buyHouse; public KeBuyHouseProxy(BuyHouse buyHouse) { this.buyHouse = buyHouse; } @Override public void buy(double totalPrice) { System.out.println("ke先了解购房者的需求"); System.out.println("ke确认购买房屋"); System.out.println("ke帮忙砍价"); //最终执行还是yxkong自己买房,只不过在yxkong买房的基础上增加了一些其他功能 buyHouse.buy(totalPrice); System.out.println("ke收取买家手续费"); System.out.println("ke收取卖家手续费"); } }

这上面的代码实现就是一个静态代理的实现。静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

动态代理

那我们能不能不一个个的创建代理类来实现代理呢?答案肯定是可以的

JDK动态代理

jdk给我们提供了一套机制

原始类,还是原来的,我们用jdk实现动态代理

public class JDKBuyHouseHandler implements InvocationHandler { private Object targetObj; public JDKBuyHouseHandler(Object targetObj) { this.targetObj = targetObj; } private void beforeBuy(){ System.out.println("中介先了解购房者的需求"); System.out.println("中介确认购买房屋"); System.out.println("中介帮忙砍价"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeBuy(); Object obj = method.invoke(this.targetObj,args); afterBuy(); return obj; } private void afterBuy(){ System.out.println("中介收取买家手续费"); System.out.println("中介收取卖家手续费"); } } // 代理工厂,只要同一类型的事,我们只要一套流程机制 public class ProxyFactory { public Object createProxy(Object targetObj) { //1,获得目标对象的所有接口 Class[] interfaces = targetObj.getClass().getInterfaces(); //2,通过jdk的InvocationHandler定义代理的扩展模板handler处理器 JDKBuyHouseHandler handler = new JDKBuyHouseHandler(targetObj); //3,通过Proxy 利用目标对象的classLoader创建代理实例 return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), interfaces, handler); } public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory(); //中介可以代理鱼翔空,也可以代理别人的买房 BuyHouse buyHouse = (BuyHouse)proxyFactory.createProxy(new YxkongBuyHouse()); buyHouse.buy(1000000); } }输出结果 中介先了解购房者的需求 中介确认购买房屋 中介帮忙砍价 yxkong买了100平,三室总价为1000000.0的房子 中介收取买家手续费 中介收取卖家手续费

JDK Proxy采用字节码重组,重新生成新的对象来替代原始的对象以达到动态代理的目的,JDK Proxy生成对象的步骤如下:

  • 通过实现InvocationHandler接口定义自己的InvocationHandler;
  • 通过Proxy.newProxyInstance 获得动态代理对象(直接绕过了)

也可以通过Proxy.getProxyClass生成代理类,然后通过反射的机制获取实例和执行方法。

看下代码

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { //InvocationHandler 必须存在 Objects.requireNonNull(h); // final Class<?>[] intfs = interfaces.clone(); /* * 创建代理类 */ Class<?> cl = getProxyClass0(loader, intfs); //获取代理类的构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); //实例化 return cons.newInstance(new Object[]{h}); }

感兴趣的可以深入的翻一翻源码。

CGLIB 动态代理

不知大家发现了没,上面不管是静态代理还是动态代理,都需要目标对象必须实现接口。但是现实情况并不是所有的目标对象都是通过面向接口编程的。

这时,我们可以使用CGLIB,使用目标对象子类的方式实现代理。

CGLIB核心类

net.sf.cglib.proxy.Enhancer – 主要的增强类 net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现 net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用: Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。 net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法 public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable; 第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

还以买房为例

public class CglibBuyHouseProxy implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { beforeBuy(); System.out.println(method.getName()); //spring的aop就是用这种方式 Object result = proxy.invoke(obj,args); // 这种方式不会纳入spring的ioc管理,因为执行的父方法 // Object result = proxy.invokeSuper(obj,args); afterBuy(); return result; } private void beforeBuy(){ System.out.println("中介先了解购房者的需求"); System.out.println("中介确认购买房屋"); System.out.println("中介帮忙砍价"); } private void afterBuy(){ System.out.println("中介收取买家手续费"); System.out.println("中介收取卖家手续费"); } public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/yxk/logs/"); CglibBuyHouseProxy proxy = new CglibBuyHouseProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(YxkongBuyHouse.class); enhancer.setCallback(proxy); YxkongBuyHouse buyHouseProxy =(YxkongBuyHouse) enhancer.create(); buyHouseProxy.buy(1880000); } } CGLIB debugging enabled, writing to '/Users/yxk/logs/' 中介先了解购房者的需求 中介确认购买房屋 中介帮忙砍价 buy yxkong买了100平,三室总价为1880000.0的房子 中介收取买家手续费 中介收取卖家手续费

把接口去掉,也可以的,大家可以试试。

不管是jdk proxy还是cglib,都是在运行时执行的。

还有一种技术手段,我觉得也可以当成是动态代理的一种。

字节码增强技术。

比如利用byte-buddy。这种手段是在类加载的时候,通过插桩的方式增强目标类的功能。相对来说比较麻烦。

大家可以看下之前的线程池监控的实现。

线程池监控-bytebuddy-agent模式

如果你对文章比较满意,可以关注公众号:5ycode


    你可能想看:

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

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

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

    分享给朋友:

    “代理模式详解产品代理模式” 的相关文章

    回国节点使用指南:轻松访问国内网站,畅享便捷网络体验

    回国节点的定义与作用 回国节点是一种特殊的网络技术,它允许用户通过位于中国境外的服务器访问国内被限制的网站或服务。这种技术对于那些在国外生活或旅行,但仍需要访问中国大陆网站的人来说非常有用。回国节点的主要作用是绕过地理限制,让用户能够像在国内一样自由地浏览和使用各种在线资源。 使用回国节点,用户不仅...

    樱花VPS:高性价比的日本虚拟专用服务器推荐

    樱花VPS是一个由日本知名主机提供商Sakura Internet Inc.推出的虚拟专用服务器产品。在选择VPS时,用户常常关注服务的稳定性、速度和价格等方面,而樱花VPS正是在这些核心领域表现出色,吸引了一大批用户的关注。 首先,樱花VPS以其高性价比闻名。作为国内前三强的VPS提供商。其规模化...

    国内VPS安装Docker的详细步骤与优化技巧

    在决定开始安装Docker之前,首先需要为你的国内VPS做好一些准备工作。准备工作不仅可以帮助我们顺利完成Docker的安装,还能让过程更加高效。 首先,选择一个适合的VPS服务提供商至关重要。目前市场上有很多VPS服务商,例如阿里云、腾讯云、Linode等。在选择时,可以根据自己的需求考虑价格、性...

    全面解析VPS测评:如何选择最佳虚拟专用服务器

    了解VPS(虚拟专用服务器)对许多人来说并不陌生。在我们的网络环境中,VPS作为一种重要的服务器解决方案,广泛应用于网站托管、应用开发、以及各种在线服务的支持。VPS让用户可以在共享环境中获得类似独立服务器的资源,提供了灵活性和更好的性能。与共享主机相比,VPS的显著优势在于更高的资源保障和自定义能...

    RFCHOST评论:高性能VPS与流媒体解锁的完美选择

    RFCHOST概述 RFCHOST是一家自2015年成立的公司,隶属于上海花卷科技。作为一家新兴的网络服务企业,RFCHOST专注于提供国际线路深层挖掘与构造网络通信服务的一体化解决方案。我一直关注着这个快速发展的品牌,尤其是它在香港和洛杉矶VPS业务上的持续投入与创新。 随着全球数字化进程的加速,...

    如何使用restorecon命令恢复Linux系统文件的安全上下文

    在学习Linux或进行系统管理时,可能会听到“restorecon”这个词。简单来说,restorecon是一个用于恢复文件和目录的安全上下文的命令。它的核心作用在于确保系统文件符合安全政策,帮助保持系统的安全性。想象一下,当我们对系统文件进行更改或更新时,这些文件的安全设置可能会被意外改变,这时r...