Java中的静态代理与动态代理

本来肝完通信编程的文章后想紧接着来一篇RPC的文章的,但是一想 RPC的话,还涉及到动态代理的知识,所以先来理一下动态代理的知识。

代理模式想必大家耳熟能详,一个代理类持有目标对象的引用,在执行目标方法前后加一点别的逻辑。其实现实中也有很多代理场景,像专利代理,房产中介代理等,今天就以房产中介为例来讲述下代理模式。

静态代理

直接用一个java类来代理另一个java类的形式就叫做静态代理,需要我们手动将代理的类编写出来。

以租房举例,租客就是目标对象,负责签字,付款。中介就是代理对象,负责带看、讲价、准备合同等工作。

首先定义一个租房的接口,租客和中介都要实现这个接口

public interface RentHouse {void rent();
}

租客类

public class Renter implements RentHouse{@Overridepublic void rent() {System.out.println("签字,交押金、租金");}
}

中介类

public class RentHouseProxy implements RentHouse {private RentHouse renter;public RentHouseProxy(RentHouse renter) {this.renter = renter;}@Overridepublic void rent() {System.out.println("带看房源,砍价,准备合同");renter.rent();System.out.println("交易完成");}}

可以看到,中介类里拥有租客对象的引用,两个类都实现了RentHouse接口,所以都实现了rent方法。中介负责代理整个租房流程,带看、砍价、准备合同、让租客交钱、完成交易。

用一个测试方法来执行下整个流程

@Test
public void staticProxyTest() {Renter renter = new Renter();RentHouseProxy rentHouseProxy = new RentHouseProxy(renter);rentHouseProxy.rent();
}

输出结果:

带看房源,砍价,准备合同
签字,交押金、租金
交易完成

这就是一个简单的静态代理,优点是逻辑很明了,缺点是扩展性差。比如中介不仅仅代理租房的业务,还代理买卖房子的业务,要实现这个业务就需要再搞一个接口出来

买房接口

public interface BuyHouse {void buy();
}

买方类

public class Buyer implements BuyHouse {@Overridepublic void buy() {System.out.println("签字,交房款");}
}

中介类

public class BuyHouseProxy implements BuyHouse {private BuyHouse buyer;public BuyHouseProxy(BuyHouse buyer) {this.buyer = buyer;}@Overridepublic void buy() {System.out.println("带看房源,砍价,准备合同");buyer.buy();System.out.println("交易完成");}}

用一个测试方法来执行下整个流程

@Test
public void staticProxyTest() {Renter renter = new Renter();RentHouseProxy rentHouseProxy = new RentHouseProxy(renter);rentHouseProxy.rent();Buyer buyer = new Buyer();BuyHouseProxy houseAgent = new BuyHouseProxy(buyer);houseAgent.buy();
}

输出结果

带看房源,砍价,准备合同
签字,交押金、租金
交易完成
带看房源,砍价,准备合同
签字,交房款
交易完成

看,单从我们这段代码来看,中介干的事其实差不多。但是为了代理租房和买房的业务,却编写了两个代理类出来。

如果中介又有了新的功能,需要再编写第三个,第四个代理类出来。有没有更好的实现方式呢?有,那就是动态代理!

动态代理

Java中动态代理最常见的就是JDK动态代理CGLIB动态代理。来看看怎么用这两种方式分别实现中介代理类。

JDK动态代理

JDK动态代理是JDK自带的实现,被代理的类需要实现一个接口,才能使用JDK动态代理。这个代理模式创建代理对象的方法为

需要三个参数

  • loader:创建代理类需要的类加载器
  • interfaces:需要代理的接口,所以说JDK动态代理必须要有接口才行
  • h:InvocationHandler接口,需要一个具体的实现类来编写代理逻辑

InvocationHandler只有一个方法需要实现

参数说明

  • proxy:代理对象(不是被代理的对象)
  • method:当前执行的方法
  • args:当前执行方法附带的参数

来看一下中介代理类的实现

public class JdkProxy implements InvocationHandler {private Object target;public JdkProxy(Object target) {this.target = target;}/*** 调用代理人对象的所有方法都会导向到下面的invoke方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("带看房源,砍价,准备合同");Object result = method.invoke(target, args);System.out.println("交易完成");return result;}
}

target是被代理的对象,可以是租客也可以是买房者。

用一个测试方法来编写租房和买房的逻辑

@Test
public void jdkProxyTest() {BuyHouse buyer = new Buyer();BuyHouse proxyBuyer = (BuyHouse) Proxy.newProxyInstance(Buyer.class.getClassLoader(), new Class[]{BuyHouse.class}, new JdkProxy(buyer));proxyBuyer.buy();RentHouse renter = new Renter();RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(Renter.class.getClassLoader(), new Class[]{RentHouse.class}, new JdkProxy(renter));rentHouse.rent();
}

输出结果:

带看房源,砍价,准备合同
签字,交房款
交易完成
带看房源,砍价,准备合同
签字,交押金、租金
交易完成

结果和使用两个静态代理类一样,但是我们只实实在在 在代码里编写了一个代理类。

另外,JDK代理也可以只代理接口,无需有实现类,这种方式在实现客户端RPC调用时比较常见。伪代码如下:

public class JdkProxyV2 implements InvocationHandler {/*** 调用代理人对象的所有方法都会导向到下面的invoke方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String interfaceName = method.getClass().getName();RpcRequest rpcRequest = new RpcRequest();rpcRequest.setClassName(interfaceName);rpcRequest.setMethodName(method.getName());rpcRequest.setParameterTypes(method.getParameterTypes());rpcRequest.setParameters(args);// 调用远程服务获取方法的执行结果Object result = RemoteCall.send(rpcRequest);return result;}
}

真实可用的代码,我会放到下篇文章编写RPC Demo时再介绍,敬请关注!

CGLIB动态代理

JDK动态代理有一个特性就是,需要有接口存在才能创建代理对象,如果没有接口的话就用不了了。而CGLIB代理就可以直接代理一个类,代理出来的类就是被代理类的子类。

由于JDK中没有CGLIB的功能,所以需要单独引入cglib的包,maven工程的话引入如下依赖

另外Spring框架中也有自己的cglib实现,如果你是在一个Spring项目中编写demo,那就省事了,不用额外引入cglib的包了。

编写cglib代理类需要实现MethodInterceptor接口,此时,中介代理类如下

public class CglibProxy implements MethodInterceptor {private Object target;public CglibProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("带看房源,砍价,准备合同");Object result = method.invoke(target, args);System.out.println("交易完成");return result;}@SuppressWarnings("unchecked")public <T> T getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return (T) enhancer.create();}}

target是目标对象,可以是租客,也可以是买房者。

intercept方法就是编写代理逻辑的方法

getProxy是我自定义的方法,这个方法演示了如何通过Enhancer来创建一个代理类。

用一个测试方法来编写租房和买房的逻辑

@Test
public void cglibProxyTest() {BuyHouse buyer = new Buyer();BuyHouse buyerProxy = new CglibProxy(buyer).getProxy();buyerProxy.buy();RentHouse renter = new Renter();RentHouse renterProxy = new CglibProxy(renter).getProxy();renterProxy.rent();
}

输出结果

带看房源,砍价,准备合同
签字,交房款
交易完成
带看房源,砍价,准备合同
签字,交押金、租金
交易完成

输出结果与静态代理和JDK动态代理并无差别。但是代理类也是只有一个

动态代理在Spring的AOP实现和众多RPC框架中非常常见,明白了使用方法不至于你在看源码时一脸懵逼。

好,Java中的代理就讲述到这里,敬请期待下篇文章,RPC篇

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

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

相关文章

开源模型应用落地-工具使用篇-Spring AI-高阶用法(九)

一、前言 通过“开源模型应用落地-工具使用篇-Spring AI-Function Call&#xff08;八&#xff09;-CSDN博客”文章的学习&#xff0c;已经掌握了如何通过Spring AI集成OpenAI以及如何进行function call的调用&#xff0c;现在将进一步学习Spring AI更高阶的用法&#xff0c;如…

易点易动固定资产管理系统如何为企业固定资产管理保驾护航

固定资产作为企业重要的资产组成部分,它直接影响企业的资金流动和经营发展。然而在传统的管理模式下,固定资产管理效率低下、盲区众多,资产利用率和价值体现不佳。易点易动智能化固定资产管理系统应运而生,为企业固定资产管理保驾护航,帮助企业实现资产高效管控、规避经营风险。…

数组与指针之二——二级指针之一

定义是这样&#xff1a; 多级指针&#xff08;二级指针&#xff09;&#xff0c;C语言多级指针的用法详解 (biancheng.net) 这是针对变量&#xff0c;且是一级一级的取的。但是我们经常要面对数组&#xff0c;用到二级指针。如前面第一篇所述&#xff0c;对一维数组名取地址&…

用于回归的概率模型

机器学习中的回归方法&#xff1a; 机器学习中的概率模型 机器学习&#xff5c;总结了11种非线性回归模型&#xff08;理论代码可视化&#xff09; 高斯过程回归&#xff1a; Gaussian Processes for Machine Learning GPML——Datasets and Code Gaussian Processes 学…

windows@查看主机名@查看IP地址

文章目录 计算机名获取ip地址方式0(最可靠)方式1方式2 查看到多个ip安装了vmware其他情况 计算机名 开始菜单中直接搜索计算机名可以进入到设置查看 更通用的办法是打开cmd或powershell 输入hostname.exe PS>HOSTNAME.EXE ColorfulCxxu返回的ColorfulCxxu就是计算机名 或…

EdgeX Foundry - 连接 Modbus 设备

文章目录 一、概述1.安装说明2.Modbus Slave 工具 二、连接 Modbus 设备1.docker-comepse2.设备配置文件3.启动 EdgeX Foundry4.访问 UI4.1. consul4.2. EdgeX Console 5.创建 Modbus 设备5.1.创建设备配置文件5.2.添加设备 6.测试6.1.命令6.2.事件6.3.读值 EdgeX Foundry # E…

Navicat连接数据库出现的问题

Navicat使用教程——连接/新建数据库、SQL实现表的创建/数据插入、解决报错【2059-authentication plugin‘caching_sha2_password’……】_2059authentication plugin-CSDN博客

优思学院|拉丁方实验设计是什么?

今天&#xff0c;我要给大家带来一个六西格玛实验设计的小窍门——拉丁方设计。这是一种巧妙的方式&#xff0c;帮助我们探索不同因素&#xff08;输入&#xff09;对结果&#xff08;输出&#xff09;的影响&#xff0c;同时巧妙地处理那些我们不想要的“噪音因素”。 想象一…

融资项目——网关微服务

1. 网关的路由转发功能 在前后端分离的项目中&#xff0c;网关服务可以将前端的相关请求转发到相应的后端微服务中。 2. 网关微服务的配置 首先需要创建一个网关微服务&#xff0c;并添加依赖。 <!-- 网关 --><dependency><groupId>org.springframework.cl…

了解开源可视化表单的主要优势

为什么可视化表单深受大家喜爱&#xff1f;这就需要了解开源可视化表单的优势和特点了。在流程化办公深入人心的今天&#xff0c;提高办公协作效率早已成为大家的发展目标&#xff0c;低代码技术平台、开源可视化表单是提升办公协作效率的得力助手&#xff0c;一起来看看它的优…

mysql的语法学习总结3(一些常见的问题)

执行后&#xff0c;MySQL 会重新加载授权表并更新权限。 FLUSH PRIVILEGES; 怎么检查自己的电脑端口3306有没有被占用&#xff1f; ESTABLISHED表示被占用&#xff0c;LISTENING表示端口正在被监听&#xff0c;22696是占用该端口的进程的PID&#xff08;进程标识符&#xff0…

DNDC模型下载与安装、建模方法、结果分析、率定验证

目录 专题一 DNDC模型介绍 专题二 DNDC初步操作 专题三 遥感和GIS基础 专题四 DNDC气象数据 专题五 DNDC土地数据 专题六 DNDC土壤数据 专题七 DNDC结果分析 专题八 DNDC率定验证 专题九 土壤碳储量与作物产量 专题十 温室气体排放分析 专题十一 农田减排潜力分析 …