代理模式详解

1.代理模式的作用

能通过代理对象间接实现对目标对象的访问,在不改变源代码的情况下对目标对象的方法进行增强。

什么是通过代理对象间接实现对目标对象的访问?

举个生活中的例子:例如你买车是通过4s店(代理对象),而不是直接去车工厂(目标对象),你只需访问4s店。

那什么不改变源代码的情况下对目标对象的方法进行增强?

还是4s店和车工厂的例子:

你去4s店买车,4s店除了卖车给你,它还有一系列的服务,例如:推荐保险,上车牌等等;

但是你去工厂就只能买车,没有其他的附加服务;也就是说4s店(代理对象)在车工厂(目标对象)的卖车基础上进行了一系列增强,但是车厂还是一样的卖车,并没有被改变。

2.代理模式的分类

代理模式分为静态代理和动态代理。

2.1静态代理

2.1.1静态代理的实现

实现步骤

1.代理类与目标类实现同一接口

2.代理对象有目标对象的引用,在同名方法中调用目标对象的方法,并在前后根据需求进行增强。

实现代码

接口代码

public interface AInterface {void say();
}

目标类代码

public class AInterfaceImpl implements AInterface{@Overridepublic void rap() {System.out.println("rap");}@Overridepublic void basketball() {System.out.println("篮球");}
}

代理类代码

public class AStaticProxy implements AInterface{//目标对象private AInterface aInterface;//注入目标对象public AStaticProxy(AInterface aInterface) {this.aInterface = aInterface;}@Overridepublic void rap() {System.out.println("前置增强");aInterface.rap();System.out.println("后置增强");}@Overridepublic void basketball() {System.out.println("前置增强");aInterface.basketball();System.out.println("后置增强");}
}

2.2.1静态代理的缺点

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

2.2动态代理

针对静态代理的缺点,我们可能观察到,许多静态代理的缺点都来自于代理类;

如果我们将代理类干掉,就能解决静态代理的大部分缺点。

可是在静态代理中我们通过代理类的构造方法才能得到代理对象,若是没有代理类,怎么创建代理对象呢?

我们先观察一下到底静态代理是如何通过代理类得到代理对象的,这就涉及到java代码的运行原理了。

           编译                         类加载                             反射得到构造方法

代理类-------> 代理类.class---------->   JVM中的Class------------------------>创建得到代理对象

那么动态代理没有代理类,只能通过接口.class,很明显会在得到构造方法这里卡住,因为接口没有构造方法。

那么有没有办法能够既有接口的方法信息又有构造方法呢?

答案是:通过Proxy类的getProxyClass(classLoader,Interface)方法,我们只需传入将接口的信息作为参数传入,就能得到一个既有接口信息又有构造方法的Class对象,通过该Class得到构造方法,我们就能创建代理对象了。

那为什么Proxy类的getProxyClass方法那么神奇?

这里尚且不做深入了解,你可以理解为它就是 想办法把接口Class里面的方法信息拼接一个构造方法得到新的Class。

2.2.1动态代理的实现

实现步骤

1.编写一个工具类,里面提供一个静态方法getProxy(Object target,InvocationHandler i)

2.在getProxy()方法中,通过Proxy类的静态方法getProxyClass(classLoader,Interface),传入目标对象接口的参数,然后得到新的代理Class对象

3.通过代理Class对象调用getDeclaredConstructor(InvocationHandler.class)方法,传入Invocationhandler的Class对象得到构造方法对象

4.通过构造方法对象的newInstance(InvocationHandler)传入Invocationhandler对象创建得到代理对象

实现代码

接口代码

public interface AInterface {void say();
}

目标类代码

public class AInterfaceImpl implements AInterface{@Overridepublic void rap() {System.out.println("rap");}@Overridepublic void basketball() {System.out.println("篮球");}
}

代理工具类的代码

public class MyProxy {/*** 获取代理对象* @param target 目标对象* @param invocationHandler 代理处理器* @return* @throws Exception*/public static Object getProxy(Object target, InvocationHandler invocationHandler) throws Exception{//获得目标对象的Class对象Class clazz = target.getClass();//根据目标对象的Class对象得到代理对象的ClassClass proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());//根据代理对象Class获得代理对象的构造器Constructor declaredConstructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);//根据代理对象的构造器创建代理对象Object o = declaredConstructor.newInstance(invocationHandler);//返回得到的代理对象return o;}
}

得到的代理对象的结构是怎么样的?

代理对象大致结构:
public class MProxy implements AInterface{private InvocationHandler ih;public MProxy(InvocationHandler ih) {this.ih = ih;}@Overridepublic void rap() {ih.invoke();}@Overridepublic void basketball() {ih.invoke();}
}

2.2.2动态代理的缺点

只剩下增强代码和目标方法硬编码

静态代理的缺点有:

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

动态代理如何优化的

对于静态代理的缺点1,动态代理只传入目标对象,目标对象可以是任意接口的,所以解决了

对于静态代理的缺点2、3、4,动态代理没有代理类,也解决了

但是对于静态代理的缺点5增强代码和目标方法硬编码,在动态代理的invoke方法

中还是没能避免,这个缺点在后来的aop(面向切面编程才得以解决),它将方法的逻辑进行拆分,无侵入式的对方法进行增强。

2.2.3动态代理的拓展

本文章的动态代理是JDK根据接口实现的,此外还有第三方的动态代理,如cglib,它实现的原理同JDK不同,它是基于继承来实现的,子类继承父类重写父类方法实现的增强

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

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

相关文章

leetcode 116. 填充每个节点的下一个右侧节点指针

leetcode 116. 填充每个节点的下一个右侧节点指针 题目 给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下: struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next …

QT 关于QTableWidget的常规使用

目录 一、初始化 二、封装功能用法 三、结语 一、初始化 1、设置表头 直接在ui设计界面修改或者使用QT封装的函数修改,代码如下: QStringList recList {"第一列", "第二列", "第三列"}; ui->tableWidget->setH…

STM32 智能家居自动化控制系统教程

目录 引言环境准备智能家居自动化控制系统基础代码实现:实现智能家居自动化控制系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景:家居控制与优化问题解决方案与优化收尾与总结 1. 引言 智能家…

【第一天】计算机网络 TCP/IP模型和OSI模型,从输入URL到页面显示发生了什么

TCP/IP模型和OSI模型 这两个模型属于计算机网络的体系结构。 OSI模型是七层模型,从上到下包括: 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层 TCP/IP模型是四层模型&…

谷粒商城实战笔记-52~53-商品服务-API-三级分类-新增-修改

文章目录 一,52-商品服务-API-三级分类-新增-新增效果完成1,点击Append按钮,显示弹窗2,测试完整代码 二,53-商品服务-API-三级分类-修改-修改效果完成1,添加Edit按钮并绑定事件2,修改弹窗确定按…

C++学习笔记01-语法基础(问题-解答自查版)

前言 以下问题以Q&A形式记录,基本上都是笔者在初学一轮后,掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系,也适合做查漏补缺和复盘。 本文对读者可以用作自查,答案在后面&#xff0…

解决llama_index中使用Ollama出现timed out 问题

现象: File "~/anaconda3/envs/leo_py38/lib/python3.8/site-packages/httpx/_transports/default.py", line 86, in map_httpcore_exceptionsraise mapped_exc(message) from exc httpx.ReadTimeout: timed out代码: from llama_index.core …

反转链表 - 力扣(LeetCode)

206. 反转链表 - 力扣(LeetCode) /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* reverseList(struct ListNode* head) {if(head NULL)return NULL;else{struct Lis…

【iOS】——属性关键字的底层原理

strong,retain,copy,atomic,nonatomic c源码 interface propertyTest : NSObject property (nonatomic, strong) NSString *nsstring___StrongTest; property (nonatomic, retain) NSString *nsstring___RetainTest; property (n…

STM32--HAL库--定时器篇

一:如何配置定时器 打开对应工程串口配置好的工程(上一篇博客)做如下配置: 定时器的中断溢出时间计算公式是: 由图得T100*1000/100MHz 注:100MHz100000000 所以溢出时间等于1ms 关于上图4的自动重装…

ARM功耗管理之Suspend-to-RAM实验

安全之安全(security)博客目录导读 ARM功耗管理精讲与实战汇总参见:Arm功耗管理精讲与实战 思考:睡眠唤醒实验?压力测试?Suspend-to-Idle/RAM/Disk演示? 1、实验环境准备 2、软件代码准备 3、唤醒源 4、Suspen…

计算机技术基础 (bat 批处理)Note4

计算机技术基础 (bat 批处理)Note4 本节主要讲解一些 bat 批处理文件中的一些特殊符号,包括 , %, > 和 >>, |, ^, & 和 && 和 ||, " ", ,, ;, ()。 回显屏蔽符 回显屏蔽符 : 这个字符在批处理中的意思是关…

把redis用在Java项目

1. Java连接redis Java连接redis的方式是通过jedis&#xff0c;连接redis需要遵循jedis协议。 1.1 引入依赖 <!--引入java连接redis的驱动--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version&…

linux 部署flask项目

linux python环境安装: https://blog.csdn.net/weixin_41934979/article/details/140528410 1.创建虚拟环境 python3.12 -m venv .venv 2.激活环境 . .venv/bin/activate 3.安装依赖包(pip3.12 install -r requirements.txt) pip3.12 install -r requirements.txt 4.测试启…

SpringBoot 后端接收参数优化(统一处理前端参数)

在使用 SpringBoot MyBatisPlus 框架的项目&#xff0c;写了个后端统一处理前端post提交的json格式的查询参数类&#xff0c;赖得手工一个一个参数处理&#xff08;把要查询的参数交给前端&#xff09;。 使用示例代码&#xff1a; PostMapping("/list")public Strin…

PyQt5 数据库操作详细教程

PyQt5 数据库操作详细教程 PyQt5 提供了丰富的数据库操作功能&#xff0c;使得在图形用户界面&#xff08;GUI&#xff09;应用程序中进行数据库连接和操作变得简单。本教程将详细介绍如何使用 PyQt5 连接和操作数据库&#xff0c;包括 SQLite、MySQL 等&#xff0c;并通过丰富…

门控循环单元(GRU)及其预测和分类Python实现

门控循环单元&#xff08;Gated Recurrent Unit&#xff0c;简称GRU&#xff09;是一种常用于处理序列数据的循环神经网络&#xff08;RNN&#xff09;变种。GRU模型结合了长短时记忆网络&#xff08;LSTM&#xff09;和标准循环神经网络的优点&#xff0c;通过门控机制帮助网络…

深入解析Perl的正则表达式:功能、应用与技巧

在编程世界中&#xff0c;正则表达式是一种强大的文本处理工具&#xff0c;它能够用于搜索、替换、匹配字符串等操作。Perl语言以其强大的文本处理能力著称&#xff0c;而其正则表达式功能更是其核心特性之一。本文将深入探讨Perl中的正则表达式&#xff0c;包括其基本语法、应…

微服务安全——OAuth2详解、授权码模式、SpringAuthorizationServer实战、SSO单点登录、Gateway整合OAuth2

文章目录 Spring Authorization Server介绍OAuth2.0协议介绍角色OAuth2.0协议的运行流程应用场景授权模式详解客户端模式密码模式授权码模式简化模式token刷新模式 OAuth 2.1 协议介绍授权码模式PKCE扩展设备授权码模式拓展授权模式 OpenID Connect 1.0协议Spring Authorizatio…

EXO-chatgpt_api 解释

目录 chatgpt_api 解释 resolve_tinygrad_tokenizer 函数 resolve_tokenizer 函数 调试和日志记录 参数 返回值 初始化方法 __init__ 异步方法 注意事项 chatgpt_api 解释 展示了如何在一个项目中组织和导入各种库、模块和类,以及如何进行一些基本的Web服务设置和配置…