第一篇:MyBatis 到底解决了什么问题?为什么 JDBC 会被它淘汰?
Mapper 接口为什么没有实现类却能执行 SQL如果你刚接触 MyBatis。可能不会觉得这有什么问题。但如果你认真想一下其实这件事非常离谱。我们平时写 JavapublicinterfaceUserService{UsergetById(Longid);}接口是不能直接执行的。因为接口没有实现。所以必须写ServicepublicclassUserServiceImplimplementsUserService{OverridepublicUsergetById(Longid){returnnull;}}然后才能调用userService.getById(1L);这是所有 Java 开发都知道的事情。但到了 MyBatis。事情突然变了。我们只写一个接口MapperpublicinterfaceUserMapper{UserselectById(Longid);}再写一条 SQLselectidselectByIdselect * from user where id #{id}/select然后直接调用userMapper.selectById(1L);竟然成功执行了。问题来了。接口没有实现类。那到底是谁执行了这个方法先思考一个问题假设让你设计 MyBatis。用户写userMapper.selectById(1L);你会怎么实现最简单的方法当然是publicclassUserMapperImplimplementsUserMapper{OverridepublicUserselectById(Longid){returnjdbcTemplate.queryForObject(...);}}但是很快你会发现。项目里可能有100个Mapper 1000个Mapper 甚至5000个Mapper难道每个 Mapper 都生成一个实现类显然不现实。MyBatis 的答案动态代理MyBatis 发现。所有 Mapper 干的事情其实都一样。无非就是接收参数 找到对应SQL 执行SQL 返回结果既然逻辑一样。那根本没必要生成大量实现类。只需要生成一个代理对象即可。代理对象到底是什么假设你调用userMapper.selectById(1L);实际上执行的并不是UserMapper而是com.sun.proxy.$ProxyXX类似这样UserMappermapperproxy;当方法被调用时mapper.selectById(1L);最终进入invoke()方法。publicObjectinvoke(Objectproxy,Methodmethod,Object[]args){}这里才是真正的入口。MyBatis 怎么知道执行哪条 SQL假设调用userMapper.selectById(1L);MyBatis 会拿到method.getName()结果selectById然后结合UserMapper.class拼出唯一标识com.demo.mapper.UserMapper.selectById这个字符串非常重要。因为在启动时。MyBatis 已经把 XML 中的 SQL 全部解析好了。例如selectidselectByIdselect * from user where id #{id}/select最终会保存成com.demo.mapper.UserMapper.selectById ↓ SQL这样就建立起了映射关系。真正执行 SQL 的是谁很多人以为MapperProxy 负责执行 SQL。其实不是。MapperProxy 只是中间人。它最终会调用SqlSession源码链路大概如下MapperProxy ↓ MapperMethod ↓ SqlSession ↓ Executor ↓ JDBC ↓ Database真正执行 SQL 的核心其实是Executor后面我们会详细分析。看一眼源码Mapper 的代理对象创建入口MapperProxyFactory核心代码protectedTnewInstance(MapperProxyTmapperProxy){return(T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{mapperInterface},mapperProxy);}是不是很熟悉上一篇 Spring 系列讲动态代理时。我们已经见过类似代码。本质上都是JDKDynamicProxy为什么这样设计如果不用动态代理。假设系统有300个Mapper就需要300个实现类维护成本极高。而使用动态代理以后一个 MapperProxy就能代理所有 Mapper 接口。这也是 MyBatis 设计最巧妙的地方。总结很多人以为userMapper.selectById()是在调用接口。实际上不是。真正执行的是 MyBatis 创建的代理对象。整个流程如下Mapper接口 ↓ JDK动态代理 ↓ MapperProxy ↓ MapperMethod ↓ SqlSession ↓ Executor ↓ 数据库所以Mapper 接口之所以没有实现类还能执行 SQL本质上是因为 MyBatis 在运行期间为它创建了动态代理对象。这也是整个 MyBatis 框架最核心的设计之一。上一篇《Spring 系列完结》下一篇《Mapper 接口为什么没有实现类却能执行 SQL》