设计模式-代理模式(静态代理,动态代理)

news2025/1/23 23:06:14

定义

代理模式Proxy是⼀种结构型设计模式,能够增强一些功能,不会影响到之前核心功能的流程。

结构图

1 通过实现接口的方式
在这里插入图片描述
2 通过继承类的方式
在这里插入图片描述

代理模式与装饰器模式

IT老齐白话设计模式
装饰和代理有着相似的结构, 但是其意图却⾮常不同。 这两个模式的构建都基于组合原则, 也就是说⼀个对象应该将部分⼯作委派给另⼀个对象。 两者之间的不同之处在于代理通常⾃⾏管理其服务对象的⽣命周期, ⽽装饰的⽣成则总是由客户端进⾏控制。 装饰器强调功能扩展,⽽代理模式⽤于已有功能的控制。

应用场景

原文:设计模式之美
1 非核心逻辑
代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。
2 代理模式在 RPC、缓存中的应用
RPC框架中消费者调用提供者发服务,通过代理模式可以屏蔽网络通信,编解码等多种细节。使得就像调用本地方法一样调用远程的方法。
假设我们要开发一个接口请求的缓存功能,对于某些接口请求,如果入参相同,在设定的过期时间内,直接返回缓存结果,而不用重新进行逻辑。这个时候就可以通过动态代理来实现,AOP的原理也是动态代理,缓存的功能可以通过AOP来实现。
原文:IT老齐白话设计模式
3 延迟初始化 (虚拟代理)
如果你有⼀个偶尔使⽤的重量级服务对象, ⼀直保持该对象运⾏会消耗系统资源时, 可使⽤代理模式。 你⽆需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
4 访问控制
如果你只希望特定客户端使⽤服务对象, 这⾥的对象可以是操作系统中⾮常重要的部分, ⽽客户端则是各种已启动的程序 (包括恶意程序), 此时可使⽤代理模式。代理可仅在客户端凭据满⾜要求时将请求传递给服务对象。

代码DEMO

1 静态代理

public interface GoodService {
    Good search(String name);
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Good {
    private String name;
    private double price;
}
public class GoodServiceImpl implements GoodService{
    private static List<Good> local;
    static {
        local = new ArrayList<Good>(){{
            add(new Good("phone", 10000));
            add(new Good("watch", 1000));
            add(new Good("mac", 1000));
        }};
    }
    @Override
    public Good search(final String name) {
        Optional<Good> first = local.stream().filter(good -> good.getName().equals(name)).findFirst();
        return first.get();
    }
}

比如我们想要在查询的时候打印一些话,也就是日志功能,我们可以新建一个代理如下:

public class GoodServiceProxy implements GoodService{
    private final GoodService goodService;

    public GoodServiceProxy() {
        // 静态代理在构造函数初始化
        this.goodService = new GoodServiceImpl();
    }
    @Override
    public Good search(String name) {
        System.out.println("==================== begin search ================");
        Good search = this.goodService.search(name);
        if (search != null && search.getPrice() >= 10000) {
            System.out.println("on my god, it is so expensive.");
        }
        System.out.println(JSONUtil.toJsonStr(search));
        return search;
    }
}
public class Client {
    public static void main(String[] args) {
        GoodServiceProxy proxy = new GoodServiceProxy();
        Good search = proxy.search("phone");
    }
}

2 JDK动态代理
我们使用java提供的动态代理语法:

public class DynamicGoodProxy implements InvocationHandler {
    private final Object targetObj;

    public DynamicGoodProxy(Object targetObj) {
        this.targetObj = targetObj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("==================== begin search ================");
        Object search = method.invoke(targetObj, args);
        System.out.println(JSONUtil.toJsonStr(search));
        System.out.println("==================== end search ================");
        return search;
    }
}
GoodService service = (GoodService) Proxy.newProxyInstance(GoodService.class.getClassLoader(), new Class[]{GoodService.class}, new DynamicGoodProxy(new GoodServiceImpl()));
Good search = service.search("phone");

原理分析:
我们首先增加JVM参数: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 这样生成的动态代理类会写到一个文件中。

// 我们可以看到JVM给我们创建了一个类继承了Proxy 并且 实现了GoodService 
public final class $Proxy0 extends Proxy implements GoodService {
	// 通过反射把方法都放在这里,通过反射创建
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    // Proxy.newProxyInstance() 会先检查缓存有没有这个类有则不重新生成 没有则重新生产 生产以后会直接NEW一个生成类的对象
    // 可以看下面截图部分
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    // 关键在于这里,在客户端我们拿到的对象实际上是生产的代理对象的实例,这个时候如果调用search
    // 实际上调用的是DynamicGoodProxy的Invoke方法
    public final Good search(String var1) throws  {
        try {
            return (Good)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.liyong.learn.proxy.demo.GoodService").getMethod("search", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

在这里插入图片描述
总结:在使用JDK动态代理的时候首先JDK会给我们生产一个代理类,然后返回代理类的实例对象
3 CGLib
cglib是一款优秀的Java 字节码生成框架,它可以生成并操纵Java字节码(底层基于ASM)。
使用这个方法将CGLib生成的动态代理对象保存下来:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);

public class CGlibGoodServiceProxy {
    private final static Enhancer enhancer = new Enhancer();
    public static  <T> T getProxy(Class<?> targeClass, MethodInterceptor methodInterceptor) {
        enhancer.setSuperclass(targeClass);
        enhancer.setCallback(methodInterceptor);
        return (T)enhancer.create();
    }
}
public class GoodServiceCallBack implements MethodInterceptor {
    /**
     *
     * @param obj "this", the enhanced object 被增强的对象
     * @param method intercepted Method 被增强的方法
     * @param args argument array; primitive types are wrapped 参数
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("==================== begin search ================");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println(JSONUtil.toJsonStr(result));
        System.out.println("==================== end search ================");
        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\学习\\model-design-demo\\");
       GoodService service =  CGlibGoodServiceProxy.getProxy(GoodServiceImpl.class, new GoodServiceCallBack());
       Good search = service.search("phone");
    }
}

可以看到底层是通过继承来实现的:
在这里插入图片描述
调用Search方法的时候实际上是转发到了GoodServiceCallBack
在这里插入图片描述
在这里插入图片描述
这个callback是通过调用set方法设置上去的:
在这里插入图片描述

最终转发到GoodServiceCallBack
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1474580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云4核8G服务器优惠价格表(轻量+CVM)

腾讯云4核8G服务器多少钱&#xff1f;轻量应用服务器4核8G12M带宽一年446元、646元15个月&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;在txy.wiki可以查询详细配置和精准报价…

DAY12_VUE基本用法详细版

目录 0 HBuilderX酷黑主题修改注释颜色1 VUE1.1 VUE介绍1.2 Vue优点1.3 VUE入门案例1.3.1 导入JS文件1.3.2 VUE入门案例 1.4 VUE基本用法1.4.1 v-cloak属性1.4.2 v-text指令1.4.3 v-html指令1.4.4 v-pre指令1.4.5 v-once指令1.4.6 v-model指令1.4.7 MVVM思想 1.5 事件绑定1.5.1…

C# OpenCvSharp DNN Yolov8-OBB 旋转目标检测

目录 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN Yolov8-OBB 旋转目标检测 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-02-26T08:38:44.171849 description&#xff1a;Ultralytics YOLOv8s-obb model trained on runs/DOT…

【.Net 使用阿里云OSS 存储文件】

一、使用NuGet安装【Aliyun.OSS.SDK】 注意&#xff1a;如果有多个项目&#xff0c;需要在具体使用的项目跟启动项目都安装同一版本的Aliyun.OSS.SDK 二、上传代码 using Aliyun.OSS; using System.IO; using System; using CadApplication.Service.Dto; using System.Net; us…

Excel 使用空格或TAB分列

1 选择“数据”>“分列”。 在“文本分列向导”中&#xff0c;选择“分隔符号”>“下一步”。 选择数据的“分隔符”。 例如&#xff0c;“逗号”和“空格”。 可在“数据预览”窗口预览数据。 选择“下一步”&#xff0c;在工作表 目标&#xff0c;在工作表中想显示拆分…

51单片机-(定时/计数器)

51单片机-&#xff08;定时/计数器&#xff09; 了解CPU时序、特殊功能寄存器和定时/计数器工作原理&#xff0c;以定时器0实现每次间隔一秒亮灯一秒的实验为例理解定时/计数器的编程实现。 1.CPU时序 1.1.四个周期 振荡周期&#xff1a;为单片机提供定时信号的振荡源的周期…

go环境安装-基于vscode的Windows安装

1、vscode安装 官网链接&#xff1a;https://code.visualstudio.com/ 选择相应的版本&#xff0c;这里选择Windows下的 下载得到一个VSCodeUserSetUp-x64的可执行文件&#xff0c;双击执行&#xff0c;选择要安装的路径&#xff0c;下一步。 2、go语言安装 官网链接&#x…

node.js提取excel中的信息填充到word文件,批量生成合同

1.npm下载 npm i pizzip docxtemplater xlsx 2.excel模板 3.word模板 4.代码 // 引入所需模块 var PizZip require(pizzip); var Docxtemplater require(docxtemplater); var fs require(fs); var path require(path); var xl require(xlsx);// 读取并导出Excel文件 …

langChain学习笔记(待续)

目录 IntroductionLLM的限制扩展理解&#xff1a;什么是机器学习扩展阅读&#xff1a;机器学习的流程 LangChain Introduction LLM的限制 大型语言模型&#xff0c;比如ChatGpt4&#xff0c;尽管已经非常强大&#xff0c;但是仍然存在一些限制&#xff1a; 知识更新&#xff…

开源现场总线协议栈(ethercat、ethernet/ip、opc ua、profinet、canopen、modbus)

ecat主站及其相关&#xff1a; 1.soem&#xff1a;GitHub - OpenEtherCATsociety/SOEM: Simple Open Source EtherCAT MasterSimple Open Source EtherCAT Master. Contribute to OpenEtherCATsociety/SOEM development by creating an account on GitHub.https://github.com/…

ARM系列 -- 虚拟化(一)

今天来研究一个有意思的话题&#xff0c;虚拟化&#xff08;virtualization&#xff09;。 开始前&#xff0c;先闲扯一下&#xff0c;最近一个词比较火&#xff0c;“元宇宙&#xff08;Metaverse&#xff09;”。在维基百科里面是这么定义元宇宙的&#xff0c;“The Metaver…

MSSQL 获取表对应的列明,备注,字段类型

旧系统代码CV多了想解放一下双手写个代码生成器&#xff0c;这时候就需要获取到表的某一些信息了 SELECT a.NAME AS colname,CONCAT(UPPER(SUBSTRING(b.name, 1, 1)), LOWER(SUBSTRING(b.name, 2,LEN(b.name)-1))) AS typename,a.length AS length,a.scale AS scale, a.prec A…

京东数据分析(电商数据分析):2024年1月京东白酒TOP10品牌销量销额排行榜

在公布2024年1月京东白酒品牌排行榜之前&#xff0c;分享一个有点意思的现象&#xff1a;在今年龙年春晚“黄金5分钟”的广告片里&#xff0c;白酒局知名的品牌基本都亮相了&#xff08;茅台、五粮液、洋河股份、郎酒、古井贡酒、水井坊&#xff09;&#xff0c;但今年汾酒却缺…

Thread多线程(创建,方法,安全,通信,线程池,并发,并行,线程的生命周期)【全详解】

目录 1.多线程概述 2.多线程的创建 3.Thread的常用方法 4.线程安全 5.线程同步 6.线程通信 7.线程池 8.其它细节知识&#xff1a;并发、并行 9.其它细节知识&#xff1a;线程的生命周期 1.多线程概述 线程是什么&#xff1f; 线程(Thread)是一个程序内部的一条执行…

安全运营中心(SOC)综合指南

什么是安全运营中心&#xff08;SOC&#xff09; 安全运营中心&#xff0c;也称为信息安全运营中心 &#xff08;ISOC&#xff09;&#xff0c;是结构良好的网络安全战略的核心。安全运营中心是一个集中式枢纽&#xff0c;无论是在组织内部还是外包&#xff0c;都致力于对整个…

非线性优化资料整理

做课题看了一些非线性优化的资料&#xff0c;整理一下&#xff0c;以方便查看&#xff1a; 优化的中文博客 数值优化|笔记整理&#xff08;8&#xff09;——带约束优化&#xff1a;引入&#xff0c;梯度投影法 (附代码)QP求解器对比对于MPC的QP求解器 数值优化| 二次规划的…

Linux命名管道

Linux匿名管道-CSDN博客 目录 1.原理 2.接口实现 3.模拟日志 Linux匿名管道-CSDN博客 这上面叫的是匿名管道&#xff0c;不要将两者搞混&#xff0c;匿名管道说的是两个有血缘关系的进程相互通信&#xff0c;但是命名管道就是两个没有关系的管道相互通信。 1.原理 和匿名…

Mysql的备份还原

模拟环境准备 创建一个名为school的数据库&#xff0c;创建一个名为Stuent的学生信息表 mysql> create database school; Query OK, 1 row affected (0.00 sec)mysql> use school; Database changed mysql> CREATE TABLE Student (-> Sno int(10) NOT NULL COMME…

Leetcode3045. 统计前后缀下标对 II

Every day a Leetcode 题目来源&#xff1a;3045. 统计前后缀下标对 II 解法1&#xff1a;字典树 将这个列表哈希化&#xff1a;idx (s[i] - ‘a’) * 26 (s[j] - ‘a’)。 枚举 twords[j]&#xff0c;怎么统计有多少个 swords[i] 是 t 的前缀&#xff1f; 这可以用字典树…

[Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包

一、设置应用包名 在Flutter开发中&#xff0c;修改应用程序的包名&#xff08;也称作Application ID&#xff09;涉及几个步骤&#xff0c;因为包名是在项目的Android和iOS平台代码中分别配置的。请按照以下步骤操作&#xff1a; 1.Android Flutter工程中全局搜索替换包名 …