最近的工作主要围绕低代码平台的 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)
图解与实战
为了更直观地理解上述流程,可以参考下面的机制图解:

接下来,我们通过一个完整的代码示例来串联整个流程。理解动态代理是深入 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 对象,在调用 save 和 queryUserAge 方法时,并没有直接执行 UserServiceImpl 中的代码,而是先进入了我们自定义的 UserProxyInvocationHandler.invoke 方法。在这里,我们成功地“拦截”了方法调用,并添加了前置和后置的日志逻辑,最后才通过反射 method.invoke(target, args) 执行了原始对象的方法。
这就是 Java 动态代理的基本原理和实战过程。它通过接口和反射机制,在运行时“无中生有”地创建出代理类,为方法调用提供了统一的增强入口。理解了这一点,就为我们后续剖析 MyBatis 中如何利用动态代理生成 Mapper 接口的实现类打下了坚实的基础。更多深入的技术讨论和实践,欢迎来 云栈社区 交流。