Java中的代理模式(动态代理和静态代理)

代理模式

我们先了解一下代理模式:

在开发中,当我们要访问目标类时,不是直接访问目标类,而是访问器代理类。通过代理类调用目标类完成操作。简单来说就是:把直接访问变为间接访问。

这样做的最大好处就是:我们可以在代理类调用目标类之前和之后去添加一些预处理和后处理操作。来扩展一些不属于目标类的功能。

比如说:我们可以在方法开始和结束前记录日志:在方法执行前进行额外的参数校验,进行事务管理,如手动提交、权限校验等。

代理模式是一种设计思想,实际实现方式上有静态代理动态代理之分。

1.静态代理

静态代理:在程序运行前,我们就给目标类编写了其代理类的代码,然后编译了其代理类。这样在程序运行之前,我们就已经生成了它代理类的字节码文件,即我们事先编写,然后编译,在程序运行的时候直接去读这些字节码文件进行运行。

例:定义静态代理类

使用静态代理类:

这里使用student调用dowork和使用staticProxy调用dowork效果不一样,后者在原有功能基础上增加了调用方法前和调用方法后的打印功能。

这就我们代理模式的最大特点:它可以控制对原有对象的访问。在原有对象的访问的基础上去做一些额外的能力。

这些类已经被编译为字节码文件了,我们可以拿这几个文件去任何一个机器上执行。

如果是静态代理的话,我们需要编写一个与其绑定的代理类。这个类会被编译成字节码文件,然后再运行。

2.动态代理

而如果是动态代理的话,我们就不需要是献给目标去编写代理代码,而是在运行中通过反射自动生成代理对象!

在Java中,动态代理主要通过两种机制实现:JDK动态代理和CGLIB动态代理。

2.1JDK动态代理

JDK动态代理基于Java的反射机制,它只能为接口创建代理对象。要使用JDK动态代理,需要实现java.lang.reflect.InvocationHandler接口,并重写其invoke()方法。invoke()方法会在代理对象上的方法被调用时被执行。

例:

这里最核心的就是通过Proxy.newProxyInstance方法去生成动态代理类以及访问它的实例。

使用动态代理:

这里dynamicProxyList就是动态代理对象,它会自动调用动态代理类DynamicProxy重写的invoke方法,打印两次开始执行是因为调用了dynamicProxyList的toString方法。

在这里我们并没有编写ArrayList的代理类,但是却把它代理了,这就是动态代理的魅力。

问题来了:这个它的动态代理类生成在哪里呢?我们怎么没看见呢?

原理:

@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {Objects.requireNonNull(h);Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);}
2.1.1caller

        caller是一个内部类,用于代表创建代理实例的调用者。这个caller类实际上是一个

InvocationHandler的实现,它负责在代理实例上转发方法调用。

newProxyInstance方法的实现中,caller类并不是直接作为参数传递给newProxyInstance方法的,而是在内部被创建和使用。这个类通常是一个匿名内部类,它的主要作用是持有对原始InvocationHandler的引用,并将方法调用转发给该处理器。

2.1.2cons

        在 Java 中,`Proxy.newProxyInstance()` 方法的源码中的 `cons` 表示 `constructor`,它用来表示代理类的构造函数。在 `Proxy.newProxyInstance()` 方法内部,会调用代理类的构造函数来创建实际的代理对象。

具体来说,`Proxy.newProxyInstance()` 方法的源码中会根据传入的类加载器(`ClassLoader`)、接口数组(`Class[]`)和 `InvocationHandler` 对象来动态生成代理类,并通过代理类的构造函数来创建代理对象。生成的代理类会实现传入的接口,并将接口中的方法调用委托给传入的 `InvocationHandler` 对象来处理。

代理类的构造函数在代理对象实例化时会被调用,并在内部完成代理对象的初始化工作,比如将 `InvocationHandler` 对象赋值给代理对象。

因此,在 `Proxy.newProxyInstance()` 方法源码中的 `cons` 所指的就是代理类的构造函数,用来创建代理对象并完成代理对象的初始化工作。

实现思路:代理类(运行时生成的代理类)和被代理类(这里就是ArrayList)实现同一个接口,有被代理类的所有方法,然后代理类把被代理类所有方法原先的调用通通先去调用代理类的InvocationHandler(也就是我们自己写的那个代理类)

在这个例子中共有32个Method(方法)对象,对应的就是List的32个方法。

如这里某个方法进行代理,我们可以看到这里它调用的是h的invoke方法,这个h就是我们之前写的InvocationHandler的实现类(DynamicProxy(我们自己写的实现Invocation的动态代理类)和这个$Proxy0代理类是两个东西!!!)  这样目标类的所有方法都会走我们写的那个通用代理类(DynamicProxy)的invoke方法。

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

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

相关文章

吴恩达机器学习-可选实验室:Softmax函数

文章目录 CostTensorflow稀疏类别交叉熵或类别交叉熵祝贺 在这个实验室里&#xff0c;我们将探索softmax函数。当解决多类分类问题时&#xff0c;该函数用于Softmax回归和神经网络。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyl…

面向低成本巡线机器人的PID控制器优化——文末源码

目录 介绍 测试 电子元器件 系统特征 控制器设计 位置误差的计算 比例控制 积分控制 微分控制 改进的PID控制器 测试轨迹 源码链接 本文对经典PID控制器的改进和开环控制机制的发展进行了讨论&#xff0c;以提高差动轮式机器人的稳定性和鲁棒性。为了部署该算法&am…

【DP】动态规划基本解题步骤(求解台阶问题)

dp数组的定义和下标递推公式dp数组如何初始化&#xff0c;初始化也需要注意遍历顺序打印dp数组&#xff08;出现问题 对于高度为 n 的台阶&#xff0c;从下往上走&#xff0c;每一步的阶数为 1&#xff0c;2&#xff0c;3 中的一个。问要走到顶部一共有多少种走法 分析&#…

python中良好的编码规范

遵循PEP 8的常见规范&#xff1a; 缩进&#xff1a; 使用4个空格来缩进代码块&#xff0c;而不是使用制表符。 命名规范&#xff1a; 变量名应该使用小写字母&#xff0c;单词之间用下划线 _ 分隔&#xff08;snake_case&#xff09;。类名应该使用驼峰命名法&#xff08;Camel…

C++ 模板知识大全

模板 泛型编程 我们如何实现一个交换函数 我们实现了两种类型的交换函数&#xff0c;但是其实除了类型不一样&#xff0c;其他地方都是一样的。 void swap(int& a, int& b) {int tmp a;a b;b tmp; }void swap(char& a, char& b) {int tmp a;a b;b tmp…

关于DCMM评估的办理条件你知道多少?

DCMM&#xff08;数据管理能力成熟度评价模型&#xff09;评估划分为五个等级&#xff0c;自低向高依次为初始级、受管理级、稳健级、量化管理级和优化级&#xff0c;不同等级代表企业数据管理和应用的成熟度水平不同&#xff0c;证书自颁发之日起有效期3年 DCMM申报基础条件 …

香港科技大学(广州)先进材料学域可持续能源与环境学域智能制造学域博士招生宣讲会——北京专场(暨全额奖学金政策)

三个学域代表教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01;可带简历现场咨询和面试&#xff01; &#x1f4b0;一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; 报名链接&#xff1a; https://www.wjx.top/vm/wF2Mant.aspx# 地点&#xff1a;中关村皇冠…

Redis中RDB的dirty机制和AOF中的后台重写机制

RDB的dirty计数器和lastsave属性 服务器除了维护saveparams数组之外&#xff0c;还维持着一个dirty计数器,以及一个lastsave属性: 1.dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后&#xff0c;服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括…

设计模式学习笔记 - 设计模式与范式 -结构型:1.代理模式:代理在RPC、缓存、监控等场景中的应用

概述 前面几个章节&#xff0c;我们学习了设计模式中的创建型模式。创建型模式主要解决对象的创建问题&#xff0c;封装复杂的创建过程&#xff0c;解耦对象的创建代码和使用代码。 单例模式用来创建全局唯一的对象。工厂模式用来创建不同但是相关类型的对象&#xff08;继承…

文件操作3

随机读写数据文件 一、随机读写原理 在我们写数据时&#xff0c;有一个光标不断的在随着新写入的数据往后移动&#xff1b; 而读数据时&#xff0c;也有一个看不见光标&#xff0c;随着已经读完的数据&#xff0c;往后移动 这里的文件读写位置标记——可以想象成图形界面里的…

算法分析与设计复试总结(二)

以下是一些常见的问题类型&#xff1a; 基础算法概念&#xff1a; 请解释什么是算法&#xff0c;以及算法的重要性。 算法是一系列解决问题的清晰指令&#xff0c;算法代表着用系统的方法描述解决问题的策略机制。也就是说&#xff0c;能够对一定规范的输入&#xff0c;在有限…

QB PHP 多语言配置

1&#xff1a; 下载QBfast .exe 的文件 2&#xff1a; 安装的时候 &#xff0c;一定点击 仅为我 安装 而不是 所有人 3&#xff1a; 如果提示 更新就 更新 &#xff0c; 安装如2 4&#xff1a; 如果遇到 新增 或者编辑已经 配置的项目时 不起作用 &#xff1a; 右…

05:HAL-----看门狗WDT

目录 一:看门狗 1:WDT 2:独立看门狗 (IWDG) A:IWDG框图 B:IWDG_KR键寄存器 C:IWDG超时时间 D:HAl库的配置 3:窗口看门狗 (WWDG) A:WWDG框图 B:WWDG工作特性 C:WWDG超时时间 D:HAL库配置 4:独立看门狗和窗口看门狗的区别 5:数据手册 二:案例 A:独立看门狗 B:窗…

LEETCODE-DAY29

title: LEETCODE-DAY29 date: 2024-03-20 15:22:38 tags: 今日内容&#xff1a;491.递增子序列、46.全排列、47.全排列 II T1 class Solution:def backtracking(self,nums,index,path,res):if indexlen(nums):res.append(path.copy())returnfor i in range(index,len(nums))…

springboot项目学习-瑞吉外卖(4)

1.任务 这一节主要的任务是解决文件的上传和下载功能 2.文件上传 概念&#xff1a;将本地的图片上传到浏览器上面 点击文件上传&#xff0c;前端就会发送如上的请求&#xff0c;服务端应该根据URL和请求方法来处理请求 CommonController类&#xff1a; RestController Slf4j …

【第二部分--Python之基础】

一、初识 开发语言&#xff1a; 高级语言&#xff1a;Python Java PHP C# Go Ruby C ... > 字节码 低级语言&#xff1a;C 汇编 > 机器码 …

6、jenkins项目构建类型1-项目类型介绍

文章目录 一、自由风格项目二、Maven项目构建三、Pipeline流水线项目构建&#xff08;☆☆☆&#xff09; Jenkins中自动构建项目的类型有很多&#xff0c;常用的有以下三种&#xff1a; 自由风格软件项目&#xff08;FreeStyle Project&#xff09;Maven项目&#xff08;Mave…

初始Redis关联和非关联

基础篇Redis 3.初始Redis 3.1.2.关联和非关联 传统数据库的表与表之间往往存在关联&#xff0c;例如外键&#xff1a; 而非关系型数据库不存在关联关系&#xff0c;要维护关系要么靠代码中的业务逻辑&#xff0c;要么靠数据之间的耦合&#xff1a; {id: 1,name: "张三…

Maya FBX导出导入

问题描述&#xff1a; Maya 导出导入 FBX&#xff0c;设置 FBX 的 导入和导出设置 解决方案&#xff1a; 获取FBX设置 def getFBXSettings():""" get current user settings for FBX export and store them """mel.eval(FBXPushSettings;)de…

指针知识大礼包,让你的编程之路更顺畅(一)

1. 内存和地址 2. 指针变量和地址 3. 指针变量类型的意义 4. const修饰指针 5. 指针运算 6. 野指针 7. assert断⾔ 8. 指针的使⽤和传址调⽤ 正文开始 1. 内存和地址 1.1 内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&a…