【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.mzph.cn/news/793650.shtml

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

相关文章

Rust---复合数据类型之结构体

目录 结构体的使用输出结果 结构体简化创建结构体更新语法元组结构体单元结构体(unit struct)结构体中的引用使用#[derive(Debug)]再次介绍 代码综合展示 与元组不同的是,结构体可以为内部的每个字段起一个富有含义的名称,因此无需…

【74LS191/48为可预置的四位二进制加/减法计数器3-9循环显示】2022-3-19

缘由我有电路原理图,想用proteus仿真,但是数码管不亮-嵌入式-CSDN问答 74LS191为可预置的四位二进制加/减法计数器 74ls191引脚功用 RCO进位/借位输出端 MAX/MIN进位/借位输出端 CTEN计数操控端 QA-QD计数输出端 U/D计数操控端 CLK时钟输入端 LO…

【Docker笔记03】【MySQL 与 Redis的主从安装】

一、前言 本系列是根据 B 站 尚硅谷 Docker 视频 学习记录笔记。因为没有视频课件,部分内容摘自 https://www.yuque.com/tmfl/cloud/dketq0。 本系列仅为自身学习笔记记录使用,记录存在偏差,推荐阅读原视频内容或本文参考笔记。 二、Mysql …

如何加速Docker镜像的构建过程?

要加速Docker镜像的构建过程,你可以考虑以下几种方法: 使用多阶段构建:多阶段构建可以帮助你减少镜像的大小,从而加速构建过程。你可以将构建过程分为多个阶段,每个阶段只保留必要的文件和依赖,最终构建出一…

10-用PySpark建立第一个Spark RDD

目录 RDD概念RDD特点建立RDD的方式不同工具建立RDD的方式使用PySpark Shell(交互环境)建立RDD使用VSCode编程建立RDD使用Jupyter Notebook建立RDD 总结 PySpark实战笔记系列第一篇 RDD概念 Apache Spark的核心组件的基础是RDD。所谓的RDD,即弹性分布式数据集&#…

Linux 关闭防火墙命令(新手)

关闭防火墙 查看防火墙状态 systemctl status firewalld.service 临时关闭防火墙(重启失效) systemctl stop firewalld.service 永久关闭防火墙 systemctl disable firewalld.servicesudo systemctl enable firewalld,这种方式输入命令…

施耐德 Unity Pro PLC 编程软件介绍

Unity Pro 软件基本介绍 Unity Pro 是施耐德中大型 PLC 的编程软件&#xff08;<–> 对应西门子 Step7&#xff09; 支持的 PLC&#xff1a;施耐德中大型 PLC 中型 PLC&#xff1a;Premium、M340&#xff08;<–> 对应西门子 S7-300、S7-1200&#xff09;大型 PL…

【C++】探索C++中的类与对象(上)

​​ &#x1f331;博客主页&#xff1a;青竹雾色间. &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ✨人生如寄&#xff0c;多忧何为 ✨ C是一种强大的编程语言&#xff0c;其面向对象的特性使得代码结构更加清晰、易于维护和扩展。在C中&#xff0c;类与…

常见docker使用命令

#搭建镜像 “”" sudo docker build -t es_refresh:V1.20230303 . “”" #启动容器 “”" docker run -d --namepara_classify -v /etc/localtime:/etc/localtime -v /data/chenhw/multi_label_classification:/edb2vec -p 8066:8066 --gpus ‘“device0”’…

Day83:服务攻防-开发组件安全JacksonFastJson各版本XStreamCVE环境复现

目录 J2EE-组件Jackson-本地demo&CVE 代码执行 (CVE-2020-8840) 代码执行 (CVE-2020-35728&#xff09; J2EE-组件FastJson-本地demo&CVE FastJson < 1.2.24 FastJson < 1.2.47 FastJson < 1.2.80 (利用条件比较苛刻) J2EE-组件XStream-靶场&CVE …

高度不同的流体瀑布css实现方法

商城商品列表 实现瀑布流展示&#xff0c;通过flex或grid实现会导致每行中的列高度一致&#xff0c;无法达到错落有致的感觉&#xff1b; 为此需要用到&#xff1a; CSS columns 属性 columns 属性是一个简写属性&#xff0c;用于设置列宽和列数。 CSS 语法 columns: column-wi…

如何让Webots支持C#语言开发的控制器

Webots支持C、C、Java、Python、Matlab这五种语言开发控制器&#xff0c;没有直接支持C#&#xff0c;但有个同事已经用C#写了大量的机器人控制代码&#xff0c;想在不把C#代码改写成C的情况下&#xff0c;直接用webots仿真&#xff0c;那就得想想办法。(不过&#xff0c;让Chat…

字节新作:图像生成质量超越DiT

&#x1f31f;每日更新最新高质量论文&#xff0c;关注我&#xff0c;时刻关注最新大模型进展。&#x1f31f; &#x1f4cc; 元数据概览&#xff1a; 标题&#xff1a;Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction作者&#xff1a…

Java 内存占用过高应该如何优化

优化Java程序的内存占用是提高程序性能和稳定性的重要任务之一。内存占用过高可能导致系统性能下降、内存泄漏、频繁的垃圾回收等问题&#xff0c;影响程序的运行效率和用户体验。因此&#xff0c;对于Java程序内存占用过高的情况&#xff0c;需要采取一系列措施进行优化。 1.…

算法设计与分析实验报告java实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)

一、 实验目的 1&#xff0e;加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…

【随笔】Git 高级篇 -- 相对引用2(十三)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

面向对象9

面向对象9 一.内部类 1、什么是内部类&#xff1f; 将一个类A定义在另一个类B里面&#xff0c;里面的那个类A就称为内部类&#xff08;InnerClass&#xff09;&#xff0c;类B则称为外部类&#xff08;OuterClass&#xff09;。 2、为什么需要内部类&#xff1f; 具体来说…

操作系统八股|用户态和内核态

文章目录 内核态(Kernel Mode)用户态(User Mode)用户态和内核态之间的切换为什么要切换如何切换系统调用硬件中断异常处理 为了使操作系统内核提供⼀个⽆懈可击的进程抽象&#xff0c;处理器必须提供⼀种机制&#xff0c; 限制⼀个应⽤可以执⾏的指令以及他可以⽤来访问的地址…

4.4C++

1 #include <iostream> #include <cmath> using namespace std; class A{ private:int a;// 判断一个数是否为质数bool isP(int num) {if (num<2) return false;for (int i2;i<sqrt(num);i) {if (num % i 0) {return false;}}return true;} public:// 构造…

open-cd框架调试记录

源于论文Changer: Feature Interaction Is What You Need forChange Detection 源码位置&#xff1a;open-cd/README.md at main likyoo/open-cd (github.com) 同样是基于MMSegmentation框架的代码&#xff0c;不符合本人编程习惯所以一直也没有研究这东西&#xff0c;近期打…