🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)🔥🔥🔥 有兴趣可以联系我
🔥🔥🔥 文末有往期免费源码,直接领取获取(无删减,无套路)
动态代理魔法:手写MyBatis的JDK代理核心引擎
-
《手写MyBatis核心引擎:JDK动态代理实现Mapper的终极奥秘》 (突出核心地位+技术深度)
-
《百行代码实现MyBatis灵魂!JDK动态代理实战Mapper调用》 (强调简洁实现+核心价值)
-
《接口即实现!深度解析MyBatis的JDK动态代理设计》 (突出技术颠覆性+深度解析)
-
《动态代理实战:手写MyBatis的MapperProxy核心类》 (聚焦具体实现+核心组件)
-
《告别静态代理桎梏:MyBatis如何用JDK动态代理实现万用Mapper》 (对比演进+技术方案)
手写MyBatis | JDK动态代理:Mapper接口的造物主
引言: 在静态代理遭遇"类爆炸"困境后,JDK动态代理如同救世主降临。今天我们将用不到百行代码,实现MyBatis最核心的MapperProxy
,揭秘接口方法如何奇迹般转化为SQL执行。
一、动态代理实战:万能Mapper生成器
目标实现
// 1. 核心:实现InvocationHandler
public class MapperProxy implements InvocationHandler {
private final SqlSession sqlSession;
public MapperProxy(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
// 2. 所有Mapper方法调用的入口
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 3. 打印方法信息(模拟执行)
System.out.println("【动态代理拦截】调用方法: " + method.getName());
System.out.println(" 参数: " + Arrays.toString(args));
// 4. 获取SQL注解(真实MyBatis会解析XML/注解)
Select selectAnno = method.getAnnotation(Select.class);
if (selectAnno != null) {
String sql = selectAnno.value();
System.out.println(" 执行SQL: " + sql);
// 5. 模拟执行并返回结果
return new User((Integer)args[0], "动态代理用户", "proxy@mybatis.org");
}
throw new UnsupportedOperationException("未找到SQL映射: " + method.getName());
}
}
// 6. 代理对象工厂
public class MapperProxyFactory {
public static <T> T getMapper(Class<T> mapperInterface, SqlSession sqlSession) {
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[]{mapperInterface},
new MapperProxy(sqlSession)
);
}
}
// 7. 客户端调用
public class Client {
public static void main(String[] args) {
// 创建动态代理实例
UserMapper userMapper = MapperProxyFactory.getMapper(
UserMapper.class, new SqlSession());
// 接口方法直接调用!
User user = userMapper.selectById(1);
System.out.println("查询结果:" + user.getName());
}
}
控制台输出:
【动态代理拦截】调用方法: selectById
参数: [1]
执行SQL: SELECT * FROM users WHERE id = #{id}
查询结果:动态代理用户
二、JDK动态代理三大核心解剖
1. Proxy.newProxyInstance
参数详解
Proxy.newProxyInstance(
ClassLoader, // 1. 加载代理类的类加载器
Class<?>[], // 2. 代理类实现的接口数组
InvocationHandler // 3. 调用处理逻辑的实现
);
2. InvocationHandler
的核心作用
3. 运行时字节码生成原理
// 生成的代理类伪代码(JDK内部实现)
public final class $Proxy0 extends Proxy implements UserMapper {
private static Method mSelectById;
static {
mSelectById = Class.forName("UserMapper")
.getMethod("selectById", int.class);
}
public $Proxy0(InvocationHandler h) { super(h); }
public User selectById(int id) {
return (User)h.invoke(this, mSelectById, new Object[]{id});
}
}
三、静态代理 vs 动态代理:降维打击
解决"大量接口代理"问题
动态代理核心优势:
-
零接口限制:一个
InvocationHandler
处理所有Mapper接口 -
逻辑集中管理:所有代理方法执行统一入口
-
运行时扩展:动态添加新功能(如缓存、日志)
-
资源节约:避免大量代理类占用内存
四、深度思考:MyBatis的代理哲学
JDK动态代理的局限性
-
接口依赖:必须基于接口,无法代理普通类
// 尝试代理非接口类将抛出异常 Proxy.newProxyInstance(..., new Class[]{User.class}, ...); // IllegalArgumentException
-
性能损耗:方法调用需反射(实际影响微乎其微)
-
复杂性:调试困难(代理类名为
$Proxy0
)
MyBatis的实践智慧
-
MapperProxy
核心地位:-
所有Mapper调用的总入口
-
连接接口方法与SQL执行的桥梁
-
插件拦截的锚点
-
-
规避JDK代理局限性的设计:
-
通过
MapperMethod
封装具体执行逻辑 -
使用
@Mapper
注解确保只代理接口 -
缓存
Method
对象减少反射开销
-
-
动态代理工作全流程:
结语: 通过实现MapperProxy
,我们见证了JDK动态代理如何以优雅的方式解决ORM框架的核心难题。这种设计不仅避免了类爆炸问题,更为MyBatis的插件体系、延迟加载等高级特性奠定基础。
下篇预告: 我们将把动态代理与实际SQL执行连接,实现真正的数据库查询!你认为在invoke()
方法中整合JDBC最大的挑战是什么?欢迎留言讨论。
关键词: #JDK动态代理 #InvocationHandler #MapperProxy #MyBatis核心 #设计模式 #接口代理 #反射机制
往期免费源码 (无删减,无套路):🔥🔥🔥
https://pan.baidu.com/s/1sjAr08PU9Xe7MQf1gjGM5w?pwd=6666
「在线考试系统源码(含搭建教程)」 (无删减,无套路):🔥🔥🔥
链接:https://pan.quark.cn/s/96c4f00fdb43 提取码:WR6M
往期免费源码对应视频:
免费获取--SpringBoot+Vue宠物商城网站系统
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我