15 动态代理:面向接口编程,屏蔽 RPC 处理流程

简介: 动态代理是RPC实现面向接口编程的核心技术。它通过运行时生成代理类,拦截接口方法调用,自动完成远程通信细节,如序列化、网络请求等,使开发者像调用本地方法一样调用远程服务,屏蔽复杂性,提升开发体验。常用实现有JDK动态代理、Javassist、Byte Buddy等。

动态代理:面向接口编程,屏蔽 RPC 处理流程
上一讲我分享了网络通信,其实要理解起来也很简单,RPC 是用来解决两个应用之间的通信,而网络则是两台机器之间的「桥梁」,只有架好了桥梁,我们才能把请求数据从一端传输另外一端。其实关于网络通信,你只要记住一个关键字就行了——可靠的传输。
那么接着上一讲的内容,我们再来聊聊动态代理在 RPC 里面的应用。
如果我问你,你知道动态代理吗? 你可能会如数家珍般地告诉我动态代理的作用以及好处。那我现在接着问你,你在项目中用过动态代理吗?这时候可能有些人就会犹豫了。那我再换一个方式问你,你在项目中有实现过统一拦截的功能吗?比如授权认证、性能统计等等。你可能立马就会想到,我实现过呀,并且我知道可以用 Spring 的 AOP 功能来实现。
没错,进一步再想,在 Spring AOP 里面我们是怎么实现统一拦截的效果呢?并且是在我们不需要改动原有代码的前提下,还能实现非业务逻辑跟业务逻辑的解耦。这里的核心就是采用动态代理技术,通过对字节码进行增强,在方法调用的时候进行拦截,以便于在方法调用前后,增加我们需要的额外处理逻辑。
那话说回来,动态代理跟 RPC 又有什么关系呢?
远程调用的魔法
我说个具体的场景,你可能就明白了。
在项目中,当我们要使用 RPC 的时候,我们一般的做法是先找服务提供方要接口,通过 Maven 或者其他的工具把接口依赖到我们项目中。我们在编写业务逻辑的时候,如果要调用提供方的接口,我们就只需要通过依赖注入的方式把接口注入到项目中就行了,然后在代码里面直接调用接口的方法 。
我们都知道,接口里并不会包含真实的业务逻辑,业务逻辑都在服务提供方应用里,但我们通过调用接口方法,确实拿到了想要的结果,是不是感觉有点神奇呢?想一下,在 RPC 里面,我们是怎么完成这个魔术的。
这里面用到的核心技术就是前面说的动态代理。RPC 会自动给接口生成一个代理类,当我们在项目中注入接口的时候,运行过程中实际绑定的是这个接口生成的代理类。这样在接口方法被调用的时候,它实际上是被生成代理类拦截到了,这样我们就可以在生成的代理类里面,加入远程调用逻辑。
通过这种「偷梁换柱」的手法,就可以帮用户屏蔽远程调用的细节,实现像调用本地一样地调用远程的体验,整体流程如下图所示:
实现原理
动态代理在 RPC 里面的作用,就像是个魔术。现在我不妨给你揭秘一下,我们一起看看这是怎么实现的。之后,学以致用自然就不难了。
我们以 Java 为例,看一个具体例子,代码如下所示:
这段代码想表达的意思就是:给 Hello 接口生成一个动态代理类,并调用接口 say() 方法,但真实返回的值居然是来自 RealHello 里面的 invoke() 方法返回值。你看,短短 50 行的代码,就完成了这个功能,是不是还挺有意思的?
那既然重点是代理类的生成,那我们就去看下 Proxy.newProxyInstance 里面究竟发生了什么?
一起看下下面的流程图,具体代码细节你可以对照着 JDK 的源码看(上文中有类和方法,可以直接定位),我是按照 1.7.X 版本梳理的。
在生成字节码的那个地方(是 java.lang.reflect.Proxy.ProxyClassFactory#apply 中处理的生成字节码文件 ),也就是 ProxyGenerator.generateProxyClass() 方法里面,通过代码我们可以看到,里面是用参数 saveGeneratedFiles (sun.misc.ProxyGenerator#saveGeneratedFiles 这个变量取值就是上面写的那一串) 来控制是否把生成的字节码保存到本地磁盘。同时为了更直观地了解代理的本质,我们需要把参数 saveGeneratedFiles 设置成 true,但这个参数的值是由 key 为 sun.misc.ProxyGenerator.saveGeneratedFiles 的 Property 来控制的,动态生成的类会保存在工程根目录下的 com/sun/proxy 目录里面(没有继承公共的代理类,则默认的包名是这个)。现在我们找到刚才生成的 $Proxy0.class ,通过反编译工具打开 class 文件,你会看到这样的代码:
我们可以看到 $Proxy0 类里面有一个跟 Hello 一样签名的 say() 方法,其中 super.h 绑定的是刚才传入的 JDKProxy 对象(构造器中传入的 InvocationHandler var1),所以当我们调用 Hello.say() 的时候,其实它是被转发到了 JDKProxy.invoke() 。到这儿,整个魔术过程就透明了。
实现方法
其实在 Java 领域,除了 JDK 默认的 InvocationHandler 能完成代理功能,我们还有很多其他的第三方框架也可以,比如像 Javassist、Byte Buddy 这样的框架。
单纯从代理功能上来看,JDK 默认的代理功能是有一定的局限性的,它要求被代理的类只能是接口。原因是因为生成的代理类会继承 Proxy 类,但 Java 是不支持多重继承的。
这个限制在 RPC 应用场景里面还是挺要紧的,因为对于服务调用方来说,在使用 RPC 的时候本来就是面向接口来编程的,这个我们刚才在前面已经讨论过了。使用 JDK 默认的代理功能,最大的问题就是性能问题。它生成后的代理类是使用反射来完成方法调用的,而这种方式相对直接用编码调用来说,性能会降低,但好在 JDK8 及以上版本对反射调用的性能有很大的提升,所以还是可以期待一下的。
相对 JDK 自带的代理功能,Javassist 的定位是能够操纵底层字节码,所以使用起来并不简单,要生成动态代理类恐怕是有点复杂了。但好的方面是,通过 Javassist 生成字节码,不需要通过反射完成方法调用,所以性能肯定是更胜一筹的。在使用中,我们要注意一个问题,通过 Javassist 生成一个代理类后,此 CtClass 对象会被冻结起来,不允许再修改;否则,再次生成时会报错。
Byte Buddy 则属于后起之秀,在很多优秀的项目中,像 Spring、Jackson 都用到了 Byte Buddy 来完成底层代理。相比 Javassist,Byte Buddy 提供了更容易操作的 API,编写的代码可读性更高。更重要的是,生成的代理类执行速度比 Javassist 更快。
虽然以上这三种框架使用的方式相差很大,但核心原理却是差不多的,区别就只是通过什么方式生成的代理类以及在生成的代理类里面是怎么完成的方法调用。同时呢,也正是因为这些细小的差异,才导致了不同的代理框架在性能方面的表现不同。因此,我们在设计 RPC 框架的时候,还是需要进行一些比较的,具体你可以综合它们的优劣以及你的场景需求进行选择。
总结
今天我们介绍了动态代理在 RPC 里面的应用,虽然它只是一种具体实现的技术,但我觉得只有理解了方法调用是怎么被拦截的,才能厘清在 RPC 里面我们是怎么做到面向接口编程,帮助用户屏蔽 RPC 调用细节的,最终呈现给用户一个像调用本地一样去调用远程的编程体验。
既然动态代理是一种具体的技术框架,那就会涉及到选型。我们可以从这样三个角度去考虑:
因为代理类是在运行中生成的,那么代理框架生成代理类的速度、生成代理类的字节码大小等等,都会影响到其性能—— 生成的字节码越小,运行所占资源就越小。
还有就是我们生成的代理类,是用于接口方法请求拦截的,所以每次调用接口方法的时候,都会执行生成的代理类,这时 生成的代理类的执行效率就需要很高效。
最后一个是从我们的使用角度出发的,我们肯定希望选择一个使用起来很方便的代理类框架,比如我们可以考虑:API 设计是否好理解、社区活跃度、还有就是依赖复杂度等等
最后,我想再强调一下。动态代理在 RPC 里面,虽然看起来只是一个很小的技术点,但就是这个创新使得用户可以不用关注细节了。其实,我们在日常设计接口的时候也是一样的,我们会想尽一切办法把细节对调用方屏蔽,让调用方的接入尽可能的简单。这就好比,让你去设计一个商品发布的接口,你并不需要暴露给用户一些细节,比如,告诉他们商品数据是怎么存储的。
课后思考
请你设想一下,如果没有动态代理帮我们完成方法调用拦截,用户该怎么完成 RPC 调用?
答:动态代理主要是帮我们完成方法调用拦截,里面是我们对远程方法的调用逻辑(如序列化请求、反序列化请求等),如果没有代理,就需要用户自己手动来调用这些方法来实现拦截里面做的一些操作

相关文章
|
2月前
|
弹性计算 负载均衡 安全
阿里云负载均衡(SLB)全链路对接与实战指南
本文系统讲解阿里云负载均衡(SLB)的完整对接流程,涵盖ALB、CLB、NLB三大产品选型、核心原理、实例创建、服务器组配置、监听设置、健康检查、会话保持、安全加固、性能优化及常见问题排查。从基础概念到高阶配置,结合实操步骤与代码示例,帮助用户快速掌握SLB对接ECS、IDC、函数计算等后端服务的方法,实现高可用、高性能的流量分发架构。
|
9月前
|
弹性计算 运维 监控
测评:阿里云轻量应用服务器深度评测,适合新手建站使用吗?
阿里云轻量应用服务器专为新手建站设计,预装WordPress等应用,5分钟快速上线。价格优惠,38元起/年,含200M高带宽,打包计费无隐性费用。集成防火墙、监控等简化运维功能,适合个人博客、小型电商等低并发场景,是入门上云的理想选择。(238字)
782 154
|
6月前
|
人工智能 运维 安全
从海外爆红到国内跟进,Clawdbot 为什么突然火了?
Clawdbot(现更名Moltbot)是2026年初爆火的可执行AI智能体,主打“替你动手”:本地/云端部署,直连邮箱、日历、飞书等,一句话完成文件转换、远程操作等任务。它标志AI从“对话”迈向“可执行系统”,虽存隐私与成本挑战,却已开启下一代AI形态的大门。
|
8月前
|
jenkins Java 持续交付
Jenkins前置配置
本文介绍Jenkins与GitLab集成的完整配置流程:包括GitLab账号创建、SSH密钥配置、API Token生成,Jenkins中GitLab连接、凭据管理、全局Git信息设置,以及节点服务器环境搭建(JDK、Maven、Node、Docker等),并详细说明Jenkins节点通过SSH方式接入的步骤,实现自动化拉取代码、构建打包与持续集成。
|
存储 人工智能 自然语言处理
RAG 实战|用 StarRocks + DeepSeek 构建智能问答与企业知识库
本文由镜舟科技解决方案架构师石强与StarRocks TSC Member赵恒联合撰写,围绕RAG(检索增强生成)技术展开,结合DeepSeek和StarRocks构建智能问答系统。RAG通过外部知识检索与AI生成相结合,解决大模型知识静态、易编造信息的问题。文章详细介绍了系统组成、操作流程及优化方法,包括DeepSeek部署、StarRocks向量索引配置、知识存储与提取等环节,并通过代码示例演示了从文本向量化到生成回答的完整过程。最后,加入RAG机制后,系统性能显著提升,支持企业级知识库与智能客服场景。文中还提供了Web可视化界面实现方案,助力开发者快速上手。
|
9月前
|
存储 弹性计算 固态存储
阿里云服务器按量付费与包年包月收费标准,云服务器最新活动价格参考
阿里云的价格一直是用户最为关注的,阿里云这两年也多次做了降价策略,阿里云根据用户的实际需求,针对云服务器收费模式推出按量付费与包年包月两种收费模式,云服务器价格表2025最新版,轻量应用服务器38元一年起,ECS云服务器2核2G3M带宽99元1年、2核4G5M带宽199元一年,新品通用算力型u2i实例4核4G1120.44元/1年起、4核8G1170.26元/1年起,九代云服务器计算型c9i实例8核16G6037.01元/1年起、通用型g9i实例8核32G7551.94元/1年起、内存型r9i实例8核64G9937.12元/1年起。叠加阿里云推出的各种优惠券还可享受满减优惠。
|
8月前
|
存储 编解码 JSON
16 RPC 实战:剖析 gRPC 源码,动手实现一个完整的 RPC
本课通过剖析gRPC源码,实战实现完整RPC框架。从动态代理、序列化到HTTP/2协议,详解请求发送与接收流程,涵盖Stub生成、数据封装、Frame传输、Netty编解码等核心机制,助你掌握高性能RPC设计精髓。
|
8月前
|
存储 消息中间件 开发框架
8应用架构图
应用架构图是技术实现的蓝图,体现系统内外部应用间的调用与数据关系。基于业务架构,划分展现、业务、数据和基础层,明确技术选型与分层逻辑。单体架构四层分离,职责清晰;分布式架构体现SOA服务调用,突出内部集成与外部依赖。通过调用链路与边界标注,厘清系统定位,支撑产品演进。(238字)
|
SQL 关系型数据库 MySQL
MySQL如何排查和删除重复数据
该文章介绍了在MySQL中如何排查和删除重复数据的方法,包括通过组合字段生成唯一标识符以及使用子查询和聚合函数来定位并删除重复记录的具体步骤。
1429 2
|
机器学习/深度学习 自然语言处理 算法

热门文章

最新文章