java高级——动态代理

目录

  • 动态代理介绍
  • 明星代理案例实现
  • 案例分析
  • 动态代理应用场景

动态代理介绍

用一个明星的案例来解释动态代理的流程。

  1. 假设现在有一个明星坤坤,它有唱歌和跳舞的本领,作为明星是要用唱歌和跳舞来赚钱的。
  2. 但是每次做节目,唱歌的时候要准备话筒、收钱,再唱歌;跳舞的时候也要准备场地、收钱、再唱歌。
  3. 明星觉得我擅长的做的事情是唱歌,和跳舞,但是每次唱歌和跳舞之前或者之后都要做一些繁琐的事情,有点烦。
  4. 于是就找个一个经济公司,请了一个代理人,代理明星处理这些事情,如果有人想请明星演出,直接找代理人就可以了。

20240227-010757-T4.png

明星代理案例实现

根据上面的案例,可以设计一个大明星对象,来表示大明星的职责是唱歌和跳舞;声明一个明星接口来声明大明星类中有的方法代理对象也会有,大明星对象实现明显接口。

  1. 明星接口
public interface Star {String sing(String name);void dance();
}
  1. 大明星对象
public class BigStar implements Star{private String name;public BigStar(String name) {this.name = name;}public String sing(String name){System.out.println(this.name + "正在唱:" + name);return "谢谢!谢谢!";}public void dance(){System.out.println(this.name  + "正在优美的跳舞~~");}
}
  1. 生成动态代理对象
    有了上面的准备工作,需要写一个为BigStar生成动态代理对象的工具类ProxyUtil代表中介机构。使用工具类产生代理则需要用Java为开发者提供的一个生成代理对象的类叫Proxy类。注意Proxy类有多个,我们需要选择java.lang.reflect中的Proxy

通过Proxy类的newInstance(…)方法可以为实现了同一接口的类生成代理对象。 调用方法时需要传递三个参数,该方法的参数解释可以查阅API文档,如下
20240227-012000-Qo.png


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyUtil {public static Star createProxy(BigStar bigStar){/* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)参数1:用于指定一个类加载器参数2:指定生成的代理长什么样子,也就是有哪些方法参数3:用来指定生成的代理对象要干什么事情*/// Star starProxy = ProxyUtil.createProxy(s);// starProxy.sing("好日子") starProxy.dance()Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class}, new InvocationHandler() {@Override // 回调方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 代理对象要做的事情,会在这里写代码if(method.getName().equals("sing")){System.out.println("准备话筒,收钱20万");}else if(method.getName().equals("dance")){System.out.println("准备场地,收钱1000万");}return method.invoke(bigStar, args);}});return starProxy;}
}
  1. 写一个Test类调用我们写好的ProxyUtil工具类,为BigStar对象生成代理对象
public class Test {public static void main(String[] args) {BigStar s = new BigStar("大明星坤坤");Star starProxy = ProxyUtil.createProxy(s);String rs = starProxy.sing("好日子");System.out.println(rs);starProxy.dance();}
}
  1. 运行结果如下
    20240227-012407-TG.png

案例分析

Proxy类的newInstance(…)方法

newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
参数1:用于指定一个类加载器 用于加载生成的代理类 写法是固定的 背就行 一般用当前类的类加载器参数2:一个接口数组 指定生成的代理长什么样子 也就是有哪些方法 我们这里只有一个接口 把它包装成数组传进去即可参数3:用来指定生成的代理对象要干什么事情 这里传递的是一个InvocationHandler接口,因为接口不能直接创建对象 所以一般是传递一个匿名内部类对象来指定代理对象干什么事情重写invoke方法就行

Proxy代理对象的执行流程

nvoke方法是个回调方法 会被谁回调呢? 
1. 假设代理写好了调用时是会写这样的代码的:
//得到一个s的代理对象
Star starProxy = ProxyUtil.createProxy(s);
starProxy.sing("好日子")
starProxy.dance()
2. 这两个sing和dance方法会调用invoke方法!
因为代理干什么事情用invoke决定,invoke需要三个参数,所以sing和dance也会传进这三个参数
比如starProxy.sing("好日子") starProxy是第一个参数,sing是第二个,"好日子"是第三个
第一个参数java把代理对象当做一个Object:也就是starProxy
第二个参数是调用的方法:如果是sing调用 method代表的就是sing方法
第三个参数args:会把方法的参数通过一个object数组传进来(比如sing调用时就会把"好日子"传进数组)
这就是invoke三个参数的含义

动态代理应用场景

某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能。

现有如下代码:
用户业务接口UserService

/***  用户业务接口*/
public interface UserService {// 登录功能void login(String loginName,String passWord) throws Exception;// 删除用户void deleteUsers() throws Exception;// 查询用户,返回数组的形式。String[] selectUsers() throws Exception;
}

用户业务实现类UserServiceImpl

/*** 用户业务实现类(面向接口编程)*/
public class UserServiceImpl implements UserService {@Overridepublic void login(String loginName, String passWord) throws Exception {long time1 = System.currentTimeMillis();if ("admin".equals(loginName) && "123456".equals(passWord)) {System.out.println("您登录成功,欢迎光临本系统~");} else {System.out.println("您登录失败,用户名或密码错误~");}Thread.sleep(1000);long time2 = System.currentTimeMillis();System.out.println("login方法耗时:" + (time2 - time1) / 1000.0 + "s");}@Overridepublic void deleteUsers() throws Exception {long time1 = System.currentTimeMillis();System.out.println("成功删除了1万个用户~");Thread.sleep(1500);long time2 = System.currentTimeMillis();System.out.println("deleteUsers方法耗时:" + (time2 - time1) / 1000.0 + "s");}@Overridepublic String[] selectUsers() throws Exception {long time1 = System.currentTimeMillis();System.out.println("查询出了3个用户");String[] names = {"张全蛋", "李二狗", "牛爱花"};Thread.sleep(500);long time2 = System.currentTimeMillis();System.out.println("selectUsers方法耗时:" + (time2 - time1) / 1000.0 + "s");return names;}
}

会发现每一个方法中计算耗时的代码都是重复的,况且这些重复的代码并不属于UserSerivce的主要业务代码。所以接下来可以把把计算每一个方法的耗时操作,交给代理对象来做。

先在UserService类中把计算耗时的代码删除,代码如下

public class UserServiceImpl implements UserService {@Overridepublic void login(String loginName, String passWord) throws Exception {if ("admin".equals(loginName) && "123456".equals(passWord)) {System.out.println("您登录成功,欢迎光临本系统~");} else {System.out.println("您登录失败,用户名或密码错误~");}Thread.sleep(1000);}@Overridepublic void deleteUsers() throws Exception {System.out.println("成功删除了1万个用户~");Thread.sleep(1500);}@Overridepublic String[] selectUsers() throws Exception {System.out.println("查询出了3个用户");String[] names = {"张全蛋", "李二狗", "牛爱花"};Thread.sleep(500);return names;}
}

然后为UserService生成一个动态代理对象,在动态代理中调用目标方法,在调用目标方法之前和之后记录毫秒值,并计算方法运行的时间。代码如下

public class ProxyUtil {public static UserService creatProxy(UserService userService){UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{UserService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName=method.getName();if(methodName.equals("login")||methodName.equals("deleteUsers")||methodName.equals("selectUsers")){long startTime = System.currentTimeMillis();Object rs = method.invoke(userService,args);long endTime = System.currentTimeMillis();System.out.println(methodName + "方法执行耗时:"+(endTime-startTime)/1000.0+"s");return rs;}else{Object rs = method.invoke(userService,args);return rs;}}});return userServiceProxy;}
}

在测试类中为UserService创建代理对象:

public class Test {public static void main(String[] args) throws Exception{// 1、创建用户业务对象。UserService userService = ProxyUtil.createProxy(new UserServiceImpl());// 2、调用用户业务的功能。userService.login("admin", "123456");System.out.println("----------------------------------");userService.deleteUsers();System.out.println("----------------------------------");String[] names = userService.selectUsers();System.out.println("查询到的用户是:" + Arrays.toString(names));System.out.println("----------------------------------");}
}

执行结果如下所示:
20240227-015521-Dg.png

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

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

相关文章

阿里云2024年服务器2核4G配置评测_CPU内存带宽_优惠价格

阿里云2核4G服务器多少钱一年&#xff1f;2核4G服务器1个月费用多少&#xff1f;2核4G服务器30元3个月、85元一年&#xff0c;轻量应用服务器2核4G4M带宽165元一年&#xff0c;企业用户2核4G5M带宽199元一年。本文阿里云服务器网整理的2核4G参加活动的主机是ECS经济型e实例和u1…

Linux磁盘性能方法以及磁盘io性能分析

Linux磁盘性能方法以及磁盘io性能分析 1. fio压测1.1. 安装fio1.2. bs 4k iodepth 1&#xff1a;随机读/写测试&#xff0c;能反映硬盘的时延性能1.3. bs 128k iodepth 32&#xff1a;顺序读/写测试&#xff0c;能反映硬盘的吞吐性能 2. dd压测2.1. 测试纯写入性能2.2. 测试…

抖音小店新店没有体验分怎么办?怎么从零做体验分?新手商家速看

大家好&#xff0c;我是电商花花。 新手开店的体验分都不是很高&#xff0c;我们想要做店铺体验分都要从零开始做。 如果新手开店不需要怎么出体验分&#xff0c;不知道怎么提高店铺体验分的&#xff0c;都可以看一下今天的文章&#xff0c;教大家怎么做店铺的体验分。 首先&…

基于springboot + vue实现的前后端分离-汽车票网上预定系统(项目 + 论文)

项目介绍 系统是一个B/S模式系统&#xff0c;采用Spring Boot框架&#xff0c;MySQL 数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得汽车票网上预订系统管理工作系统化、规范化。本系统的使用使管理人…

JVM——JVM与Java体系结构

文章目录 1、Java及JVM简介1.1、Java是跨平台的语言1.2、JVM是跨语言的平台 2、Java发展里程碑3、Open JDK和Oracle JDK4、虚拟机与JVM4.1、虚拟机4.2、JVM 5、JVM整体结构6、Java代码执行流程7、JVM的架构模型7.1、基于栈式架构的特点7.2、基于寄存器架构的特点 8、JVM的生命周…

cRIO9040中NI9871模块的测试

硬件准备 CompactRIO9040NI9871直流电源&#xff08;可调&#xff09;网线RJ50转DB9线鸣志STF03-R驱动器和步进电机 软件安装 参考&#xff1a;cRIO9040中NI9381模块的测试 此外&#xff0c;需安装NI-Serial 9870和9871扫描引擎支持 打开NI Measurement&#xff06;Automa…

Docke相关命令总结

docker systemctl 相关 commanddetailsudo systemctl start docker启动dockersudo systemctl stop docker停止dockersudo systemctl restart docker重启dockersudo systemctl status docker查看docker状态 镜像相关 commanddetaildocker search 镜像名称搜索镜像docker pull …

jetson nano——编译安装opencv-python==4.3.0.38

目录 1.下载源码&#xff0c;我提供的链接如下&#xff1a;2.解压文件3.安装依赖scikit4.安装opencv-python5.查看opencv-python版本 系统&#xff1a;jetson-nano-jp451-sd-card-image ubuntu 18.04 1.下载源码&#xff0c;我提供的链接如下&#xff1a; 链接&#xff1a;http…

网络:IPv6

1、由于IPv4地址资源枯竭&#xff0c;所以产生了IPV6。 版本长度地址数量IPv432 bit4 294 967 296IPv6128 bit340 282 366 920 938 463 374 607 431 768 211 456 2、IPv6的基本报头在IPv4报头基础上&#xff0c;增加了流标签域&#xff0c;去除了一些冗余字段&#xff0c;使报…

docker (十二)-私有仓库

docker registry 我们可以使用docker push将自己的image推送到docker hub中进行共享&#xff0c;但是在实际工作中&#xff0c;很多公司的代码不能上传到公开的仓库中&#xff0c;因此我们可以创建自己的镜像仓库。 docker 官网提供了一个docker registry的私有仓库项目&#…

Zookeeper基础入门-2【ZooKeeper 分布式锁案例】

Zookeeper基础入门-2【ZooKeeper 分布式锁案例】 四、ZooKeeper-IDEA环境搭建4.1.环境搭建4.1.1.创建maven工程&#xff1a;zookeeper4.1.2.在pom文件添加依赖4.1.3.在项目的src/main/resources 目录下&#xff0c;新建文件为“log4j.properties”4.1.4.创建包名com.orange.zk …

Neoverse S3 系统 IP:机密计算和多芯片基础设施 SoC 的基础

第三代Neoverse系统IP Neoverse S3 产品推出了我们的第三代基础设施特定系统 IP&#xff0c;这是下一代基础设施 SOC 的理想基础&#xff0c;适用于从 HPC 和机器学习到 Edge 和 DPU 的各种应用。S3 机箱专注于为我们的合作伙伴提供 Chiplet、机密计算等关键创新以及 UCIe、DD…

(Linux学习一):Mac安装vmWare11.5,centOS 7安装步骤教程

一。下载vmware 官网地址&#xff1a;下载地址 由于我的电脑系统是Mac 10.15.6版本系统&#xff0c;我下载的是VMware Fusion 11.5版本&#xff0c;13是最新版本不支持安装需要系统在11以上。 百度网盘下载地址: VMware Fusion 11 VMware Fusion 12 VMware Fusion 13 下载需要…

matlab实现不同窗滤波器示例

1 汉明窗低通滤波器 &#xff1a; 在Matlab中使用汉明窗设计低通滤波器可以通过fir1函数实现。汉明窗通常用于设计滤波器&#xff0c;可以提供更突出的频率特性。 下面是一个示例代码&#xff0c;演示如何在Matlab中使用汉明窗设计低通滤波器&#xff1a; % 定义滤波器参数 fs …

揭秘数字证书:保护你的数据不止于表面

数字证书&#xff0c;这个看似枯燥无味的电子文件&#xff0c;其实背后隐藏着一套精密的运行机制。今天陕西CA就来给大家揭开它的神秘面纱。 首先&#xff0c;数字证书是由权威的第三方机构颁发的&#xff0c;这些机构通常被称为证书颁发机构&#xff08;CA&#xff09;&#…

python web框架fastapi模板渲染--Jinja2使用技巧总结

文章目录 1.jinja2模板1.1、jinja2 的变量1.1.1 列表类型数据渲染1.1.2 字典类型数据渲染 2. jinja2 的过滤器3. jinja2 的控制结构3.1、分支控制3.2、循环控制 1.jinja2模板 要了解jinja2&#xff0c;那么需要先理解模板的概念。模板在Python的web开发中⼴泛使⽤&#xff0c;…

双硬盘备份的一种可行方案

双硬盘备份有什么优势弊端&#xff1f; 事物总有两面性&#xff0c;那么对于双硬盘数据备份任务来说&#xff0c;有什么优势与弊端呢&#xff1f; ◉ 双硬盘备份的优势&#xff1a; 安全性更好&#xff1a;由于数据备份到两个不同的硬盘&#xff0c;所以可以保证备份数据的冗…

基于springboot实现图书馆管理系统项目【项目源码+论文说明】

基于springboot实现图书馆管理系统演示 摘要 电脑的出现是一个时代的进步&#xff0c;不仅仅帮助人们解决了一些数学上的难题&#xff0c;如今电脑的出现&#xff0c;更加方便了人们在工作和生活中对于一些事物的处理。应用的越来越广泛&#xff0c;通过互联网我们可以更方便地…

练习 1 Web EasySQL极客大挑战

CTF Week 1 EasySQL极客大挑战 BUUCTF 典中典复习 Web SQL 先尝试输入&#xff0c;找一找交互页面 check.php 尝试万能语句 a’ or true SQL注入&#xff1a;#和–的作用 get传参只能是url编码&#xff0c;注意修改编码&#xff0c;输入的字符串要改成url格式。 POST请求和…

Linux:Kubernetes(k8s)——基础理论笔记(1)

我笔记来源的图片以及共享至GitHub&#xff0c;本章纯理论。这是k8s中部分的基础理论 &#x1f447; KALItarro/k8spdf: 这个里面只有一个pdf文件 (github.com)https://github.com/KALItarro/k8spdf&#x1f446; 什么是kubernetes kubernetes 是一个开源的&#xff0c;用于管…