代理模式-动态代理

news2025/1/29 13:53:09

一、代理模式

代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3种:
Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
RealSubject(真实主题角色):真正实现业务逻辑的类;
Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

如果根据字节码的创建时机来分类,可以分为静态代理动态代理:
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运
行前就确定了。
而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代
理类的字节码文件 

 1.1静态代理

静态代理的工作原理如下:

  • 定义一个接口(或抽象类)作为目标接口,目标对象实现这个接口。
  • 创建一个代理类,实现目标接口,并持有目标对象的引用。
  • 在代理类中重写目标接口的方法,在方法调用前后执行需要的额外操作。
  • 客户端使用代理对象来访问目标对象。

代码例子: 

静态代理通过 UserServiceProxy实现,代理类同时也需要实现UserService 接口。

UserService:

public interface UserService {
	public void select();	
	public void update();
}

UserServiceImpl: 

//RealSubject 真实主题(真正的业务类)
public class UserServiceImpl implements UserService{
	@Override
	public void select() {
		System.out.println("查询selectById");		
	}
	@Override
	public void update() {
		System.out.println("更新update");
	}

}

UserServiceProxy:

//代理
public class UserServiceProxy implements UserService{

	//包含Subject真实的主题
	private UserServiceImpl realUserService =new UserServiceImpl();
	@Override
	public void select() {
		long begin=System.currentTimeMillis();	
		//调用真正的业务逻辑
		realUserService.select();
		long end=System.currentTimeMillis();
		System.out.println("select()执行耗时"+(end-begin)+"毫秒!");
	}

	@Override
	public void update() {
		realUserService.update();		
	}
}

 测试:

public class Test {
	public static void main(String[] args) {
		UserServiceProxy userServiceProxy=new UserServiceProxy();
		userServiceProxy.select();
	}
}

通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也
会暴露出来。
当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:

  • 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
  • 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护

静态代理的特点:

需要手动编写代理类,工作量较大。

目标对象必须实现接口。

代理类和目标类的关系在编译时就确定了,无法动态改变

1.2动态代理

Java 中两种常见的动态代理方式: JDK 原生动态代理和 CGLIB 动态代理(第三方开源类
库)。

动态代理的工作原理如下:

定义一个接口,作为目标接口。

创建一个InvocationHandler接口的实现类,该类负责处理方法调用并执行额外的操作。

使用Proxy类的静态方法newProxyInstance()生成代理对象,同时指定目标对象和InvocationHandler。

客户端使用代理对象来访问目标对象的方法。

 代码例子如下:

OrderService :

public interface OrderService {
	public void create(double money,int uid);

}

OrderServiceImpl: 

public class OrderServiceImpl implements OrderService {
	@Override
	public void create(double money, int uid) {
		System.out.println("订单金额:¥"+money);
		System.out.println("订单id:"+uid);
	}
}

PerformanceInvocationHandler: 

//用于监测方法执行性能的Handler执行器
public class PerformanceInvocationHandler implements InvocationHandler {
	private Object real;
	public PerformanceInvocationHandler(Object real) {
		this.real=real;
	}	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		long begin=System.currentTimeMillis();
		
		//真实业务对象当前的执行方法(基于反射的方式)
		Object returnValue=method.invoke(real, args);
		
		long end=System.currentTimeMillis();
		
		System.out.println("方式执行耗时"+(end-begin)+"毫秒!");
		return returnValue;
	}
}

Test:

public class Test {
	public static void main(String[] args) {
		//真实主题对象
		OrderServiceImpl realOrderService=new OrderServiceImpl();
		
		//获取类加载器
		ClassLoader classLoader=realOrderService.getClass().getClassLoader();
		
		//接口列表
		Class[] interfaces=realOrderService.getClass().getInterfaces();
		
		//创建InvocationHandler对象(动态代理的执行逻辑)
		PerformanceInvocationHandler p=new PerformanceInvocationHandler(realOrderService);
		
		//创建一个代理对象(动态代理对象)
		OrderService orderServiceProxy=(OrderService)Proxy.newProxyInstance(classLoader, interfaces, p);
		
		//调用方法
		orderServiceProxy.create(1345, 0001);		
	};
}

动态代理的特点:

不需要手动编写代理类,代理对象在运行时动态生成。

目标对象可以不实现接口,只需定义目标对象的共同接口。

代理对象和目标对象的关系在运行时确定,可以动态改变。

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

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

相关文章

Flutter 安装,配置,运行第一个app 1

起因, 目的: flutter, 其实几年前,我就写过。 当时纯属是个人兴趣,随意探索。 当时我也写了几篇笔记: 比如这一篇还有这个 flutter,其实不难,比较繁琐,小的知识点很多. flutter, 又是环境配…

如何使用 C# 解决 Cloudflare Turnstile CAPTCHA 挑战

处理 CAPTCHA 挑战的复杂性可能是一项艰巨的任务,尤其是在涉及 Cloudflare 的 Turnstile 时。作为一名经验丰富的开发人员,我多年来遇到了许多 CAPTCHA 系统,但 Cloudflare Turnstile 由于其旨在阻止自动化系统的复杂算法,提出了独…

Mac 搭建仓颉语言开发环境(Cangjie SDK)

文章目录 仓颉编程语言通用版本SDK Beta试用报名仓颉语言文档注册 GitCode登录 GitCode 下载 Cangjie SDK配置环境变量VSCode 插件VSCode 创建项目 仓颉编程语言通用版本SDK Beta试用报名 https://wj.qq.com/s2/14870499/c76f/ 仓颉语言文档 https://developer.huawei.com/c…

ad18学习笔记十七:如何正确打开别人给的工程文件

不要单独打开一个pcb文件,如果没有在一个工程中关联上的话,可能会出现无法复制粘贴焊盘的情况。一般别人给文件会给整个工程,要打开的话直接打开整个工程,那么工程里相互关联的几个文件就都可以操作了。 AD中,怎样把从…

Linux操作系统:GCC(GNU Compiler Collection)编译器

在 Linux 系统中,gcc(GNU Compiler Collection)是一个非常强大的编译器,主要用于编译 C 语言程序。 除了基本的编译和链接命令外,gcc还提供了许多选项和功能。 以下是一些常用的 gcc命令及其功能: 1. 基本…

WEB攻防-JavaWweb项目JWT身份攻击组件安全访问控制

知识点: 1、JavaWeb常见安全及代码逻辑; 2、目录遍历&身份验证&逻辑&JWT; 3、访问控制&安全组件&越权&三方组件; 演示案例: JavaWeb-WebGoat8靶场搭建使用 安全问题-目录遍历&身份认…

MATLAB系列09:图形句柄

MATLAB系列09:图形句柄 9. 图形句柄9.1 MATLAB图形系统9.2 对象句柄9.3 对象属性的检测和更改9.3.1 在创建对象时改变对象的属性9.3.2 对象创建后改变对象的属性 9.4 用 set 函数列出可能属性值9.5 自定义数据9.6 对象查找9.7 用鼠标选择对象9.8 位置和单位9.8.1 图…

Linux相关概念和重要知识点(4)(自举、vim)

1.语言和编译器的发展 (1)汇编语言的出现 计算机只能看懂二进制,但是用二进制实现一个功能就太难了,人们需要发明一种高效的语言。人们抽象出一套编程逻辑,定义了一系列操作,接下来就需要实现它。最初人们…

假期学习笔记总结--iOS 自动释放池

iOS 自动释放池 https://juejin.cn/post/6844904094503567368#heading-23 ARC和MRC 苹果在 iOS 5 中引入了ARC(Automatic Reference Counting)自动引用计数内存管理技术,通过LLVM编译器和Runtime协作来进行自动管理内存。LLVM编译器会在编…

Linux进阶命令-重定向

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解,我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

我的创作纪念日-20240919

何尝不是一种纪念。 话说,毕业之后和大学同学去深圳,后面回家考编制,现在在家里的中国邮政的代理金融网点上班。

C++:布尔类型,引用,堆区空间

1.布尔类型 #include <iostream>using namespace std;int main() {bool b13;bool b20;cout << "b1" <<b1<< endl;cout << "b2" <<b2<< endl;cout <<boolalpha<< "b1" <<b1<<…

CGE:基于Causal LLM的Code Embedding模型

近日&#xff0c;CodeFuse-CGE 项目在外滩大会展出&#xff0c;吸引了众多技术、产品从业者的到访&#xff0c;部分参观者表示“文搜代码”令人耳目一新&#xff0c;期待模型后续的表现。 以下是 CodeFuse-CGE 项目的相关开源介绍&#xff0c;如果对这部分内容感兴趣&#xff…

Qt 窗口事件机制

在 Qt 开发中&#xff0c;窗口的关闭、隐藏、显示等事件是常见且重要的功能。不同的事件触发条件、处理方式不同&#xff0c;了解和掌握这些事件有助于我们更好地控制窗口行为。本文将详细讲解这些事件的使用方法&#xff0c;并通过代码实例来展示其应用。 1. done(int r) — 关…

9.19总结

这几天学习了网络流 1&#xff0c;EK ek的主要思路是不断通过bfs找到增广路&#xff0c;找到增广路再建立反向边&#xff0c;直到不能再bfs到汇点&#xff0c;为什么可以通过建反向边呢&#xff1f;以上图举例&#xff0c;上图走完第一条增广路建立了一条反向边&#xff0c;当…

fps pve制作

1 导入素材 将人物模型和骨骼导入&#xff08;直接将fps拖进去&#xff0c;选择正确的骨骼即可&#xff09; 将枪支模型导入&#xff0c;取消创建骨骼&#xff0c;将静态网格体导入其中 2创建角色蓝图&#xff0c;也就是我们玩家控制的对象 然后在角色的组件中找到网格体并使…

几何 | 数学专项

日期内容2024.9.19创建 { d > 0 , 递增数列 d < 0 , 递减数列 d 0 &#xff0c;常数列 \begin{cases} d>0,递增数列\\ d<0,递减数列\\ d0&#xff0c;常数列 \end{cases} ⎩ ⎨ ⎧​d>0,递增数列d<0,递减数列d0&#xff0c;常数列​ 【2010.13】 【1.历年真…

【算法题】46. 全排列-力扣(LeetCode)

【算法题】46. 全排列-力扣(LeetCode) 1.题目 下方是力扣官方题目的地址 46. 全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3…

漏件、丢件常发生?海外仓智能化管理来解决

随着海外仓业务的迅猛发展&#xff0c;一些仓库在日常运营中出现了出入库操作不规范、库存管理不够严格等问题。这些问题多数源于高度依赖人工操作及随意性较强的管理方式&#xff0c;导致了库存数据不准确、货物遗漏或丢失等情况的发生。长此以往&#xff0c;不仅会增加客户的…

让自动驾驶系统无限逼近人类?最新混合规划器实现高度安全的实车导航

导读&#xff1a; 本篇文章针对基于学习的规划器难以保证安全闭环驾驶这一问题&#xff0c;提出了一种新型的混合运动规划器&#xff0c;其结合了基于学习和基于优化的技术。通过仿真实验和实车实验&#xff0c;证明了本文规划器的有效性和鲁棒性。©️【深蓝AI】编译 1. 摘…