找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1687

积分

0

好友

227

主题
发表于 13 小时前 | 查看: 3| 回复: 0

最近的工作主要围绕低代码平台的 ORM 模块开发,这促使我萌生了动手实现一个简化版 MyBatis 的想法。这个系列的开篇,我们将聚焦于其底层的重要基石之一——Java 动态代理

在开始之前,可以先看看小傅哥的手写Mybatis系列作为参考。

认识 java.lang.reflect.Proxy#newProxyInstance

这个方法的作用

它的核心功能,是能在运行时动态创建一个“实现了指定接口的类”的实例。这个代理实例的所有方法调用,都会被统一转发给一个 InvocationHandler 对象来处理。这正是实现 AOP(面向切面编程)、RPC 远程调用等高级特性的关键技术。

方法签名详解

先来看一下这个方法完整的签名:

public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)

下面我们逐一分析这三个参数。

1. ClassLoader loader

这个参数用于加载动态生成的代理类
通常有两种常见的取值方式:

  • 使用接口自身的类加载器:SomeInterface.class.getClassLoader()
  • 使用当前线程的上下文类加载器

2. Class<?>[] interfaces

这个参数定义了代理类需要实现哪些接口
这里有两点关键限制:

  • 数组中的元素必须是接口,不能是普通类。
  • 这是 JDK 原生动态代理机制的一个硬性规定。

示例:new Class[]{UserService.class}

3. InvocationHandler h

这是整个动态代理逻辑的核心枢纽
所有通过代理对象发起的方法调用,最终都会被路由到这个处理器的 invoke 方法中:

Object invoke(Object proxy, Method method, Object[] args)

图解与实战

为了更直观地理解上述流程,可以参考下面的机制图解:

Java动态代理机制流程图

接下来,我们通过一个完整的代码示例来串联整个流程。理解动态代理是深入 MyBatis 等框架源码,乃至进行 开源实战 的基础。

第一步:定义接口

首先,我们定义一个简单的用户服务接口。

public interface UserService {

    void save(String name);

    Integer queryUserAge(String uId);
}

第二步:实现接口

接着,提供该接口的一个具体实现类。

public class UserServiceImpl implements UserService {
    @Override
    public void save(String name) {
        System.out.println("Saving user: " + name);
    }

    @Override
    public Integer queryUserAge(String uId) {
        System.out.println("Query user age: " + uId);
        return 18;
    }
}

第三步:编写调用处理器(InvocationHandler)

这里我们创建一个处理器,它会在目标方法执行前后添加日志逻辑,这也是 AOP 的雏形。

public class UserProxyInvocationHandler implements InvocationHandler {

    /**
     * 将被代理的真实对象传入
     * 通过有参构造传递
     */
    private final Object target;

    public UserProxyInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 增强逻辑的核心方法
     *
     * @param proxy  代理对象自身
     * @param method 被调用的方法
     * @param args   方法参数
     * @return 方法执行结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法执行前增强
        System.out.println("方法之前执行...." + method.getName() + ":传递的参数..." + Arrays.toString(args));

        //执行被代理对象的原始方法
        Object res = method.invoke(target, args);

        //方法执行后增强
        System.out.println("方法之后执行..." + target);
        return res;
    }
}

第四步:组装并使用代理

最后,我们将所有部分组合起来,创建代理对象并进行调用。

public class ProxyTest {
    public static void main(String[] args) {
        // 1. 创建真实对象
        UserService target = new UserServiceImpl();

        // 2. 创建代理对象
        //    - UserService.class.getClassLoader(): 类加载器
        //    - new Class[]{UserService.class}: 要代理的接口
        //    - new UserProxyInvocationHandler(target): 调用处理器
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),
                new Class[]{UserService.class},
                new UserProxyInvocationHandler(target)
        );

        // 3. 通过代理对象调用方法
        proxy.save("Tom");

        Integer jerry = proxy.queryUserAge("Jerry");
        System.out.println("Jerry's age is: " + jerry);
    }
}

运行上述 main 方法,控制台将输出以下结果:

方法之前执行....save:传递的参数...[Tom]
Saving user: Tom
方法之后执行...com.yanx.yanbatis.base.UserServiceImpl@330bedb4
方法之前执行....queryUserAge:传递的参数...[Jerry]
Query user age: Jerry
方法之后执行...com.yanx.yanbatis.base.UserServiceImpl@330bedb4
Jerry's age is: 18

可以看到,通过 Proxy.newProxyInstance 创建的 proxy 对象,在调用 savequeryUserAge 方法时,并没有直接执行 UserServiceImpl 中的代码,而是先进入了我们自定义的 UserProxyInvocationHandler.invoke 方法。在这里,我们成功地“拦截”了方法调用,并添加了前置和后置的日志逻辑,最后才通过反射 method.invoke(target, args) 执行了原始对象的方法。

这就是 Java 动态代理的基本原理和实战过程。它通过接口和反射机制,在运行时“无中生有”地创建出代理类,为方法调用提供了统一的增强入口。理解了这一点,就为我们后续剖析 MyBatis 中如何利用动态代理生成 Mapper 接口的实现类打下了坚实的基础。更多深入的技术讨论和实践,欢迎来 云栈社区 交流。




上一篇:Spring @Transactional 源码深度剖析:连接绑定与事务管理全流程
下一篇:深入解析Mybatis代理机制:手写映射工厂与动态代理实现
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-28 22:13 , Processed in 0.399080 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表