前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >10分钟带你彻底搞懂动态代理机制

10分钟带你彻底搞懂动态代理机制

原创
作者头像
写bug的高哈哈
发布于 2025-02-02 03:55:03
发布于 2025-02-02 03:55:03
1010
举报

在面向对象的世界中,对象与对象之间的相互协作构成了系统的运行状态。通常,我们可以在一个对象中直接引用另一个对象来获取想要的功能,但有时候事情并没有那么简单。我们来看一段简单的代码示例:

代码语言:java
AI代码解释
复制
@Service
public class HealthService {
    public void recordUserHealthData(HealthData data) {
    	healthRepository.recordUserHealthData(data);
    	logger.info("Record user health data successfully.");
    }}

上述代码很简单,是在 Service 层组件中调用数据访问层组件,并记录一个操作日志。现在假设这个 HealthService 中有很多方法,而对所有方法操作都需要添加日志。显然,在每个方法里都手工调用同一个日志方法不是一种很好的解决方案,会造成代码冗余,增加维护成本。

这个时候,代理机制就可以派上用场了。我们可以构建一个代理对象,然后由这个代理对象统一实现日志记录操作。

图 1 基于代理机制的对象交互示意图
图 1 基于代理机制的对象交互示意图

可以看到,通过代理机制,一个对象就可以在承接另一个对象功能的基础之上,同时添加新的功能。相比直接在原有对象中嵌入代码,代理机制为我们提供了更为优雅的解决方案。

那么,代理机制具体是如何实现的呢?让我们一起来看一下。

代理机制实现方式

代理机制一般有两种实现方式,一种是 静态代理机制,一种是 动态代理机制。一般来说静态机制设计和实现上比较容易理解,而动态机制则较为复杂,所以我们先来学习一下静态机制。

静态代理

静态代理机制在技术上比较简单,我们先看这样一个示例。

我们考虑有一个 Account 接口,包含一个用于开设账户的 open() 方法。

代码语言:java
AI代码解释
复制
public interface Account{
    void open();
}

然后针对该接口有一个实现类 RealAccount,提供了对 open() 方法的模拟实现。

代码语言:java
AI代码解释
复制
public class RealAccount implements Account {
    private String name;
    public RealAccount(String name) {
        this.name = name;
    }
    @Override
    public void open() {
        System.out.println("开账户: " + name);
    }
}

接下来就是代理类的实现,我们称之为 ProxyAccount,代码如下所示:

代码语言:java
AI代码解释
复制
public class ProxyAccount implements Account {
    private Account account;
    private String name;
    public ProxyAccount(String name) {
        this.name = name;
    }
    @Override
    public void open() {
        checkName(name);
        if (account == null) {
           account = new RealAccount(name);
        }
        account.open();
    }
    private void checkName(String name) {
        System.out.println("验证用户名称: " + name);
    }
}

可以看到 ProxyAccount 同样实现了 Account 接口,并保存了一个 RealAccount 实例。这样,ProxyAccount 对象的 open() 方法既包含了 RealAccount 中的原有逻辑,又额外代理了验证用户名称的逻辑。它们之间的类层结构如下图所示。

图 2 静态代理类层结构图
图 2 静态代理类层结构图

跟动态代理相比,静态代理模式中的代理关系是 编码阶段就能决定的,所以容易管理,也不存在因为外部代理框架而造成的性能损耗。你在 Mybatis 的缓存和连接池等的实现机制中,都能看到静态代理模式的应用场景。

动态代理

介绍完静态代理,我们接着来看动态代理。在 Java 世界中,想要实现动态代理,可以使用 JDK 自带的代理机制。

现在假设同样存在静态代理中 Account 接口以及实现类 RealAccount,然后我们需要再调用其 open() 方法的前后记录操作日志。

在 JDK 自带的动态代理中存在一个 InvocationHandler 接口,想要实现动态代理,我们首先要做的就是提供该接口的一个实现类。

代码语言:java
AI代码解释
复制
public class AccountHandler implements InvocationHandler{
    private Object obj;
    public AccountHandler(Object obj) {
        super();
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] arg)
           throws Throwable {
        Object result = null;
        doBefore();
        result = method.invoke(obj, arg);
        doAfter();
        return result;
    }
    public void doBefore() {
        System.out.println("开户前");
    }
    public void doAfter() {
        System.out.println("开户后");
    }
}

InvocationHandler 接口中包含一个 invoke() 方法,我们必须实现这一方法。在该方法中,我们通常需要调用 method.invoke() 方法执行原有对象的代码逻辑,然后可以在该方法前后添加相应的代理实现。在上述代码中,我们只是简单打印了日志。

然后,我们编写测试类来应用上述 AccountHandler 类,如下所示。

代码语言:java
AI代码解释
复制
public class AccountTest {
    public static void main(String[] args) {
        Account account = new RealAccount("xiaoyiran");
        InvocationHandler handler = new AccountHandler(account);
        Account proxy = (Account)Proxy.newProxyInstance(
               account.getClass().getClassLoader(),
               account.getClass().getInterfaces(),
               handler);
        proxy.open();
    }
}

这里,Proxy.newProxyInstance() 方法的作用就是生成 RealAccount 类的代理类。当该方法被调用时,RealAccount 类的实例就会注入到这个代理类中。然后当代理类的 open() 方法被调用时,AccountHandler 中 invoke() 方法就会被执行,从而执行代理方法。这里的类层结构是这样的。

图 3 基于 JDK 动态代理的类层结构图
图 3 基于 JDK 动态代理的类层结构图

仔细分析上述代码结构,可以看到针对某一个业务结构,我们分别提供了一个实现类以及对应的代理类。通过 JDK 提供的动态代理机制,我们可以把这些类整合在一起。而在代码实现上,我们也可以发现其遵循这样一个流程:设计和实现业务接口→实现 Handler 类→创建代理类,然后在 Handler 类中构建具体的代理逻辑。上述流程也是代表了一种标准的代理机制实现流程。我们可以联想一下,有很多基于 AOP 机制的拦截器,它们的实现机制实际上就是类似的原理。

代理机制在开源框架中的应用

关于 JDK 自带的动态代理机制,在 Dubbo 和 Mybatis 框架中都得到了应用,其中 Dubbo 主要使用动态代理实现远程方法的调用,而 Mybatis 则基于这一机制来完成数据访问。我们将分别对这两种场景展开讨论,先来看 Dubbo 远程访问中的代理机制。

Dubbo 远程访问中的代理机制

我们在 Dubbo 代码中找到了如下所示的 JdkProxyFactory 类,用来获取代理对象。

代码语言:java
AI代码解释
复制
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}

这里你可以看到 Proxy.newProxyInstance() 方法,这是典型的 JDK 动态代理的用法。根据传入的接口获得动态代理类,当调用这些接口的方法时都会转而调用 InvokerInvocationHandler(invoker)。我们来看一下 InvokerInvocationHandler 类。基于 JDK 动态代理的实现机制,可以想象 InvokerInvocationHandler 类必定实现了 InvocationHandler 接口。

代码语言:java
AI代码解释
复制
public class InvokerInvocationHandler implements InvocationHandler {
    private final Invoker<?> invoker;
    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

可以看到,这里把方法的执行流程转向了 invoker.invoke() 方法,而这个 invoker 对象会负责执行具体的远程调用操作。

Mybatis 数据访问中的代理机制

使用过 Mybatis 的同学都知道,我们只需要定义 Mapper 层的接口而不需要对其进行具体的实现,该接口就能够正常完成 SQL 执行等一系列操作,这是怎么做到的呢?

实际上 Mybatis 能够做到这一点的背后就是使用了强大的代理机制,具体来说就是如下所示的 MapperProxy 类。

代码语言:java
AI代码解释
复制
public class MapperProxy<T> implements InvocationHandler, Serializable {
	@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
	}
}

可以看到,这里对于执行 SQL 语句的方法而言,MapperProxy 会把这部分工作交给 MapperMethod 处理。MapperMethod 会进一步调用 Mybatis 中的 SqlSession 对象并执行具体的 SQL 语句。

目前为止,我们看到了 MapperProxy 类实现了 InvocationHandler 接口,但还没有看到 Proxy.newProxyInstance() 方法的调用,该方法实际上位于 MapperProxyFactory 类中,该类还存在 newInstance() 重载方法,通过传入 mapperProxy 的代理对象最终完成代理方法的执行。

代码语言:java
AI代码解释
复制
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

作为总结,我们梳理了 Mybatis 中 Mapper 层动态代理相关类的类层结构。

图 4 Mybatis 中代理类层结构图
图 4 Mybatis 中代理类层结构图

可以看到,Mybatis 在实现 SQL 执行时使用的就是 JDK 中所提供了原生动态代理机制。通过合理划分各个组件的职责,Mybatis 设计了用来生成代理对象 MapperProxy 的代理工厂类 MapperProxyFactory,并最终把执行 SQL 的操作封装在了 MapperMethod 中。

总结

今天我们系统介绍了代理机制。在日常开发过程中,代理可以说是一种通用性非常高的实现机制,它是面向切面编程的基础,也在主流的开源框架中得到了广泛地应用。例如,Dubbo 在实现远程方法调用时就用到了动态代理,而 Mybatis 则基于它来完成数据访问。这些应用方式和实现过程值得我们学习和模仿。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java 动态代理实现 ORM
ORM(Object/Relational Mapper),即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。本文简述通过 Java 动态代理机制实现关系数据与 POJO 对象的映射。
知一
2021/12/07
6310
Java 动态代理实现 ORM
10分钟带你搞懂代理模式、静态代理、JDK+CGLIB动态代理
代理模式是一种比较好的理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
Guide哥
2020/09/27
4710
10分钟带你搞懂代理模式、静态代理、JDK+CGLIB动态代理
JDK动态代理一定要有代理对象吗?请结合Mybatis回答
有一段时间没有写文章了, 主要是回想起这两年多的时间,多多少少,每个知识点差不多都有写到了, 一时也想不起什么新鲜的知识分享给大家. 今天写动态代理,主要是在看Mybatis源码时,发现真的是把动态代理用的是太6 了, 感叹之余,有一些心得,和大家分享一下.
微笑的小小刀
2020/06/19
4200
MyBatis的动态代理实现细节
一直以来都在使用MyBatis做持久化框架,也知道当我们定义XXXMapper接口类并利用它来做CRUD操作时,Mybatis是利用了动态代理的技术帮我们生成代理类。那么动态代理内部的实现细节到底是怎么的呀?XXXMapper.java类和XXXMapper.xml到底是如何关联起来的呀?本篇文章就来详细剖析下MyBatis的动态代理的具体实现机制。
二哥聊运营工具
2021/12/17
9320
MyBatis的动态代理实现细节
设计模式学习笔记(七)代理模式以及动态代理的实现
代理模式(Proxy Design Pattern)是为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
归思君
2023/10/16
2240
设计模式学习笔记(七)代理模式以及动态代理的实现
浅析MyBatis的动态代理原理[通俗易懂]
前言 一直以来都在使用MyBatis做持久化框架,也知道当我们定义XXXMapper接口类并利用它来做CRUD操作时,Mybatis是利用了动态代理的技术帮我们生成代理类。那么动态代理内部的实现细节到底是怎么的呀?XXXMapper.java类和XXXMapper.xml到底是如何关联起来的呀?本篇文章就来详细剖析下MyBatis的动态代理的具体实现机制。
全栈程序员站长
2022/09/22
2K0
从 Spring 集成 MyBatis 到浅析 Java 动态代理
因为 MyBatis 的易上手性和可控性,使得它成为了 ORM框架中的首选。近日新起了一个项目,所以重新搭建了一下 Spring-mybatis, 下面是搭建笔记和从 Spring-mybatis源码分析其如何使用 Java动态代理,希望对大家有帮助。
Java技术江湖
2019/09/24
4650
从 Spring 集成 MyBatis 到浅析 Java 动态代理
从 Spring 集成 MyBatis 到浅析 Java 动态代理
因为 MyBatis 的易上手性和可控性,使得它成为了 ORM框架中的首选。近日新起了一个项目,所以重新搭建了一下 Spring-mybatis, 下面是搭建笔记和从 Spring-mybatis源码分析其如何使用 Java动态代理,希望对大家有帮助。
Bug开发工程师
2019/06/03
5350
带你搞懂Java动态代理
抽象角色是一个接口,代理角色和真实角色都实现这个接口,同时代理角色包含真实角色的对象并在实现的抽象角色方法里调用真实角色实现的抽象角色方法来拿访问者提的要求。
longzeqiu
2020/02/10
3760
JDK动态代理的底层实现原理
JDK动态代理的底层实现原理      动态代理是许多框架底层实现的基础,比如Spirng的AOP等,其实弄清楚了动态代理的实现原理,它就没那么神奇了,下面就来通过案例和分析JDK底层源码来揭秘她的神秘面纱,让她一丝不挂地呈现在我们面前,邪恶了。。。 代理模式定义 存在一个代理对象,并且这个代理对象持有真实对象的引用,以实现对真实对象的访问控制。 举个例子,现在公司一般都有VPN,使我们在家也能访问到公司的内网(比如连接公司的数据库等),实现居家办公。这里VPN相当于一个代理,而公司内网相当于被代理对象,也
汤高
2018/03/28
2.2K0
JDK动态代理的底层实现原理
Java | 静态代理与动态代理真的超简单
静态代理: 由我们开发者自己手动创建或者在程序运行前就已经存在的代理类,静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
Petterp
2022/02/09
4700
动态代理(1)
代理模式是一种比较好的理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
黑洞代码
2021/01/19
2410
Java 静态代理、Java动态代理、CGLIB动态代理
Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有 2 个优势:
java思维导图
2019/05/21
7.1K0
Java基础(九) 动态代理
所以动态代理,可以通过调用class类文件,然后通过反射机制生成相对应的代理类或者代理对象。
宇宙无敌暴龙战士之心悦大王
2022/02/07
2260
java的动态代理机制详解_java为什么是静态语言
说起java动态代理,在我刚开始学java时对这项技术也是十分困惑,明明可以直接调通的对象方法为什么还要使用动态代理?随着学习的不断深入和工作经验的积累,慢慢的体会并理解了java动态代理机制。昨天再给公司新同事做技术培训时有同学就对动态代理产生了疑问,我这里梳理一遍一并记录一下,方便大家查看对自己也是加深记忆。
全栈程序员站长
2022/11/07
3310
jdk的动态代理机制_jdk动态代理
摘要:   代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其所生成的代理类在将所有的方法调用分派到委托对象上反射执行的同时,还可以对方法进行增强,这也正是Spring AOP的实现基础。通过阅读本文,读者将会对代理模式和Java动态代理机制有更加深入的理解。
全栈程序员站长
2022/11/03
4770
jdk的动态代理机制_jdk动态代理
你必须会的 JDK 动态代理和 CGLIB 动态代理
我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性。通过从静态代理、JDK 动态代理、CGLIB 动态代理来进行本文的分析。
ytao
2020/06/04
5360
你必须会的 JDK 动态代理和 CGLIB 动态代理
动态代理机制
代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。 换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。 客户类真正想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。客户类对目标对象的访问是通过访问代理对象来实现的。代理类与目标类要实现同一个接口。 例如:有A,B,C三个类,A原来可以调用C类的方法,现在因为某种原因C类不允许A类调用其方法,但B类可以调用C类的方法,A类通过B类调用C类的方法。这里B是C的代理,A通过代理B访问C。 原来的访问关系:
技术交流
2022/11/18
2500
动态代理机制
jdk动态代理和cglb动态代理
静态代理是在编译时就确定了代理类的代码,在程序运行前就已经存在了代理类的class文件。代理类与委托类的关系在编译时就已经确定,因此被称为静态代理。在静态代理中,代理类需要实现与委托类相同的接口或者继承委托类的父类,以便能够对委托类进行代理操作。
一个风轻云淡
2023/10/15
2390
jdk动态代理和cglb动态代理
jdk动态代理和cglib动态代理详解
如上图,代理模式可分为动态代理和静态代理,我们比较常用的有动态代理中的jdk动态代理和Cglib代理,像spring框架、hibernate框架中都采用了JDK动态代理,下面将结合代码阐述两种代理模式的使用与区别。
全栈程序员站长
2022/08/29
7220
jdk动态代理和cglib动态代理详解
相关推荐
Java 动态代理实现 ORM
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档