【Java】jdk1.8 Java代理模式,Jdk动态代理讲解(非常详细,附带class文件)

  📝个人主页:哈__

期待您的关注 

一、什么是代理模式

想要学代理模式,我们就要先弄清一个概念“什么是代理”?

在我们的现实生活中,你或许不少听过关于代理的名词,如:代理商。那什么又叫做代理商?让我一个词来形容就是中间商。

举个例子,在你买二手房的时候,你一般不会直接和业主去交谈,你或许会找一家二手房出租出售企业来寻找房源,你看上哪个房子后,由这位中间商来沟通房源的业主完成购买流程。这就是一个代理的例子。

再举个例子,在青青草原上举办了一场选美大赛,在喜羊羊的劝导下,美羊羊参加了这场比赛。当然结果对于美羊羊来说是十分完美的,美羊羊以158票的优势超越第二名的红太狼位居榜首。从此美羊羊成为了青青草原的大明星。

红太狼自然是不服,于是灰太狼和红太狼商量了一个计划,他们想要请美羊羊来参加一场由他们举办的模特比赛,美羊羊在接收到邀请后非常开心,直接就同意了他们的邀请。但这时作为经纪人的喜羊羊就不同意了,这明显是请君入瓮啊,于是喜羊羊就代替美羊羊推掉了他们的邀请。

如果没有喜羊羊,美羊羊去了之后的后果可想而知,这时就体现出了喜羊羊的重要性了。

 

二、Java中的静态代理 

 1.创建我们的美羊羊同意邀请的service层。go()方法也叫做目标方法。

public interface YangService {void go();
}public class YangServiceImpl implements YangService {@Overridepublic void go() {System.out.println("美羊羊想要同意邀请");}
}

2.创建一个喜羊羊检查意图和拒绝他们的邀请的类(喜羊羊可能是多个人的经纪人,所以把他的功能单独抽取)。这些方法我也会称作增强方法。

public class DaoTransaction{public  void beforeCheck(){System.out.println("喜羊羊检查灰太狼的意图");}public  void afterCheck(){System.out.println("喜羊羊拒绝他们的邀请");}}

3.我们创建一个代理类,这个类就相当于灰太狼和红太狼直接向喜羊羊发出了邀请,美羊羊太火,他们无法直接接触到美羊羊。这里要实现美羊羊功能的接口,因为喜羊羊要对美羊羊的一切行动做出处理。看下边的代码,美羊羊在同意之前要经过喜羊羊的检查,同意之后还要被喜羊羊拒绝。

public class XiYyProxy implements YangService {DaoTransaction daoTransaction = new DaoTransaction();YangServiceImpl yangService = new YangServiceImpl();@Overridepublic void go() {daoTransaction.beforeCheck();yangService.go();daoTransaction.afterCheck();}
}

4.主程序。

public static void main(String[] args) {XiYyProxy xiYyProxy = new XiYyProxy();xiYyProxy.go();}

三、Jdk动态代理 

我们上边写的是静态代理,你可以看到,我们的代码只能让喜羊羊作为美羊羊一个人的经纪人,静态代理有着这么几个特点。

  1. 目标角色固定
  2. 在应用程序执行前就得到目标角色
  3. 代理对象会增强目标对象的行为
  4. 有可能存在多个代理 引起"类爆炸"(缺点)

如果灰太狼邀请的不是美羊羊,那我们的代码就毫无用处了,所以我们使用动态代理来实现功能。

我们创建这样的一个service,我们上边写的是YangService,为了区分我们创建一个YangTwoService,这里我弄得不太规范,重在让大家理解。

public interface YangTwoService {void go();
}public class YangTwoServiceImpl implements YangTwoService {@Overridepublic void go() {System.out.println("沸羊羊同意参加健美比赛");}
}

那如果我们想要喜羊羊作为沸羊羊的经纪人拒绝掉这个比赛呢?还按照上边的代理方式吗?这就太费事费力了。我们这样想,我们把喜羊羊作为代理的过程抽取出来,到底要代理谁我们后期决定,只要传入一个参数确认是谁,这样我们的代码就简化很多了。

创建JdkProxyHandler。

public class JdkProxyHandler implements InvocationHandler {//这是我们要代理的目标对象 到底是谁Object object;// 这是从喜羊羊那里抽取出来的方法DaoTransaction daoTransaction;public JdkProxyHandler(Object o,DaoTransaction daoTransaction){this.object = o;this.daoTransaction = daoTransaction;}@Override//proxy--代理类  method--目标方法   args--目标方法的参数public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object o = new Object();//我们只对go方法进行处理,喜羊羊不能管得太多if(method.getName().equals("go")){daoTransaction.beforeCheck();o = method.invoke(object,args);daoTransaction.afterCheck();}else{o = method.invoke(object,args);}return o;}
}

主方法。

public class Main {public static void main(String[] args) {DaoTransaction daoTransaction = new DaoTransaction();YangServiceImpl yangService = new YangServiceImpl();YangTwoServiceImpl yangTwoService = new YangTwoServiceImpl();JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction);JdkProxyHandler proxy2 = new JdkProxyHandler(yangTwoService,daoTransaction);YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1);YangTwoService proxyYangTwo = (YangTwoService) Proxy.newProxyInstance(yangTwoService.getClass().getClassLoader(), yangTwoService.getClass().getInterfaces(), proxy2);proxyYang.go();proxyYangTwo.go();}
}

看不懂没关系,我接下来会将Jdk动态代理的过程。我先来解释一下上边的代码。

创建我们的Handler,在上边我们能够看到,我们的逻辑处理都是在这个对象里的,这个对象很重要,你到底是如何让喜羊羊去处理的,都是靠这个JdkProxyHandler类(自定义)实现。每只不同的羊都有自己的对象。

JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction);

这行代码才是真正创建我们的代理对象,也就是相当于我们上边静态代理创建的XiYyProxy对象,我们调用美羊羊和沸羊羊的go方法,都通过这个proxyYang对象 。

 YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1);

 下边这行代码就是调用我们创建出来的代理对象来处理我们的逻辑。

proxyYang.go();

 

四、Jdk动态代理解析 

我们生成一下Jdk动态代理在编译后生成的class文件的反编译结果来查看一下。下边的代码就是为了生成我们的class文件的。

public static void saveProxyClass(String path) throws IOException {byte[] $proxy1s = ProxyGenerator.generateProxyClass("$Proxy1", YangServiceImpl.class.getInterfaces());FileOutputStream out = null;try{out = new FileOutputStream(new File(path+"$Proxy1.class"));out.write($proxy1s);}catch (Exception e){e.printStackTrace();}finally {if(out == null){try {out.flush();out.close();}catch (Exception e){e.printStackTrace();}}}}

在main方法中调用这个方法。注意:一定要修改saveProxyClass的路径,我直接生成到根目录下了!!!!!!

 public static void main(String[] args) throws IOException {DaoTransaction daoTransaction = new DaoTransaction();YangServiceImpl yangService = new YangServiceImpl();YangTwoServiceImpl yangTwoService = new YangTwoServiceImpl();JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction);JdkProxyHandler proxy2 = new JdkProxyHandler(yangTwoService,daoTransaction);YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1);YangTwoService proxyYangTwo = (YangTwoService) Proxy.newProxyInstance(yangTwoService.getClass().getClassLoader(), yangTwoService.getClass().getInterfaces(), proxy2);proxyYang.go();proxyYangTwo.go();saveProxyClass("你的真实路径");}

查看文件。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//import com.my.proxy.service.YangService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy1 extends Proxy implements YangService {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy1(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);}}public final void go() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}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.my.proxy.service.YangService").getMethod("go");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

我们来分块讲解一下,先看下边的static静态块,这个块在类被加载的时候自动执行里边的代码。

我去,这不就是反射吗把我的YangServiceImpl类中继承的Object类中的方法都给我反射出来了,同时还把我的YangService接口中的方法go()也给我反射出来了。

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.my.proxy.service.YangService").getMethod("go");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}

 在看看我们的go()方法调用。我去,这个super.h.invoke(this,m3,(Object[])null)是啥啊,我带你先弄清h是啥。

public final void go() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}

我们往上看看这个代理类的构造函数。你明白了吗?h就是我们传给父类的InvocationHandler类型的对象,也就是我们上边自己创建的JdkProxyHandler类。

public $Proxy1(InvocationHandler var1) throws  {super(var1);}

那这个go方法还用点进去吗?我们上边的JdkProxyHandler已经写清楚了。

现在你是否了解了Jdk动态代理?它是通过反射实现的,将目标接口中的方法进行反射,然后我们的代理类拿到这些方法,每当我们执行目标接口中的方法的时候,都会通过我们的代理类进行反射调用,具体如何调用,就是通过我们创建的InvocationHandler类型的对象。

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

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

相关文章

【大数据存储】实验七 Spark RDD

Spark RDD操作实验 一、实验目的 (1)掌握使用Spark访问本地文件和HDFS文件的方法 (2)熟练掌握在Spark Shell中对Spark RDD的操作方法 (3)掌握Spark应用程序的编写、编译打包和运行方法 二、.实验平台 …

【无限列车1】SpringCloudAlibaba 与 SpringBoot后端架构的搭建

【无限列车1】SpringCloudAlibaba 与 SpringBoot后端架构的搭建 1、版本说明二、日志相关配置3、AOP 打印日志4、下载开源前端后台管理系统5、添加网关模块6、集成数据库和mp(1) 添加驱动和mp依赖(2) 数据库配置(3) 使用MybatisPlus 7、加密 yaml 文件中的内容(1) 依赖(2) 敏感…

ES6: class类

类 class 面相对象class关键字创建类关于类的继承 面相对象 一切皆对象。 举例: 操作浏览器要使用window对象;操作网页要使用document对象;操作控制台要使用console对象; ES6中增加了类的概念,其实ES5中已经可以实现类…

SpringBoot整合knife4J 3.0.3

Knife4j的前身是swagger-bootstrap-ui,前身swagger-bootstrap-ui是一个纯swagger-ui的ui皮肤项目。项目正式更名为knife4j,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于前端Ui…

基于ArrayList实现简单洗牌

前言 在之前的那篇文章中,我们已经认识了顺序表—>http://t.csdnimg.cn/2I3fE 基于此,便好理解ArrayList和后面的洗牌游戏了。 什么是ArrayList? ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表&…

第二十三章 Git

一、Git Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版…

YOLOv8 UI界面设计+热力图显示

进入可视化设计界面,设计UI pyside6-designer 设计好UI保存,然后通过以下命令将ui文件保存为py pyside6-uic myui.ui > myui.py 通过以下命令将资源文件qrc保存为py pyside6-rcc my_rc.qrc > my_rc.py 写主窗口函数实现功能... 项目基于yol…

【STL】vector的底层原理及其实现

vector的介绍 vector是一个可变的数组序列容器。 1.vector的底层实际上就是一个数组。因此vector也可以采用连续存储空间来存储元素。也可以直接用下标访问vector的元素。我们完全可以把它就当成一个自定义类型的数组使用。 2.除了可以直接用下标访问元素,vector还…

Redis缓存穿透和缓存雪崩

一、缓存穿透 1 什么是缓存穿透 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数…

Spoon Taking Problem(c++题解)

题目描述 �N 人が円卓に座っており,各人は反時計回りに順に 1, …, �1, …, N と番号付けられています.各人はそれぞれ左右どちらか一方の利き手を持っています. 円卓上には 1, …, �1, …, N と番号付け…

Golang Gin框架

1、这篇文章我们简要讨论一些Gin框架 主要是给大家一个基本概念 1、Gin主要是分为路由和中间件部分。 Gin底层使用的是net/http的逻辑,net/http主要是说,当来一个网络请求时,go func开启另一个协程去处理后续(类似epoll)。 然后主协程持续…

Vue 样式技巧总结与整理[中级局]

SFC(单文件组件)由 3 个不同的实体组成:模板、脚本和样式。三者都很重要,但后者往往被忽视,即使它可能变得复杂,且经常导致挫折和 bug。 更好的理解可以改善代码审查并减少调试时间。 这里有 7 个奇技淫巧…

VB 通过COM接口解析PSD文件

最近有PS测评的需求,故而想到了解析psd文件,目的就是为了获取文档信息和图层信息;获取PS的图像信息有很多方式,有过程性的,比如监听PS的各种操作事件;有结果性的,比如本文写的解析PSD文件。 0.…

【算法练习】28:选择排序学习笔记

一、选择排序的算法思想 弄懂选择排序算法,先得知道两个概念:未排序序列,已排序序列。 原理:以升序为例,选择排序算法的思想是,先将整个序列当做未排序的序列,以序列的第一个元素开始。然后从左…

libusb Qt使用记录

1.libusb 下载 ,选择编译好的二进制文件,libusb-1.0.26-binaries.7z libusb Activity 2. 解压 3. 在 Qt Widgets Application 或者 Qt Console Application 工程中导入库,Qt 使用的是 minggw 64编译器,所以选择libusb-MinGW-x64。…

Kubernetes(k8s):精通 Pod 操作的关键命令

Kubernetes(k8s):精通 Pod 操作的关键命令 1、查看 Pod 列表2、 查看 Pod 的详细信息3、创建 Pod4、删除 Pod5、获取 Pod 日志6、进入 Pod 执行命令7、暂停和启动 Pod8、改变 Pod 副本数量9、查看当前部署中使用的镜像版本10、滚动更新 Pod11…

保研线性代数复习3

一.基底(Basis) 1.什么是生成集(Generating Set)?什么是张成空间(Span)? 存在向量空间V(V,,*),和向量集(xi是所说的列向量&#xff…

集合/容器

集合概念 当我们保存一组一样(类型相同)的元素时候,我们应该使用一个容器来存储,就可以采用数组,但是数组存在以下缺点: 1、长度开始时必须指定,一旦指定就不能更改。 2、使用数组进行增加元素的步骤比较麻烦 这时候…

深入PostgreSQL中的pg_global表空间

pg_global表空间的位置 在PG当中,一个实例(cluster)初始化完以后,你会看到有下边两个与表空间相关的目录生成: $PGDATA/base $PGDATA/global 我们再用元命令\db以及相关视图看看相应的表空间信息: postgres# \db …

Golang | Leetcode Golang题解之第10题正则表达式匹配

题目&#xff1a; 题解&#xff1a; func isMatch(s string, p string) bool {m, n : len(s), len(p)matches : func(i, j int) bool {if i 0 {return false}if p[j-1] . {return true}return s[i-1] p[j-1]}f : make([][]bool, m 1)for i : 0; i < len(f); i {f[i] m…