找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6070999|回复: 0

为啥Mybatis的接口不需要实现类,你真的有了解过吗?

[复制链接]

该用户从未签到

发表于 2021-4-6 19:03:23 | 显示全部楼层 |阅读模式

您需要 登录 才可以下载或查看,没有账号?立即注册

×
在使用mybatis框架时,我们发现mapper接口是没有实现类的,取而代之的是一个xml文件。也就是说我们调用mapper接口,实际上是使用了对应的mapper.xml中定义sql语句完成数据操作的。

那么我们有没有想过,为什么mapper接口没有实现类,它是如何和xml关联起来的?
我们在学习java 基础时,知道接口是不可以直接调用的,只有通过定义实现类了实现接口,然后new一个实现类对象,再将实现类对象赋给接口声明的对象。调用接口的方法实际上就是调用被引用对象的方法,也就是实现类实现的方法。那么为啥Mybatis的接口不需要实现类呢?
下面我们就以查询所有员工数据为例,进行一步一步的调试,查看底层是怎么操作的呢?
EmployeeMapper接口

EmployeeMapper.xml文件的sql语句

那么getAllEmp方法被调用的时候,被引用的对象是谁呢?接口被调用时候发生了什么?
我们先来回答第二个问题,既然找不到实现类,EmployeeMapper有没可能被代理起来呢,getAllEmp方法调用时候,我们找到代理对象来执行就行了。
在学习代理模式时,有一个动态代理模式,通过动态代理接口的所有方法,每次接口被调用,就会进入动态代理对象的invoke方法,然后加载xml中的sql完成操作数据库,再返回结果。
那么Mybatis 的Mapper接口有没有可能被动态代理对象来实现,完成后面的操作呢?
接下来我们进入debug模式来调试看看。
(1) 在sqlSession.getMapper方法这里设置一个断点

(2)继续走到Configuration类的getMapper方法里,Configuration主要存储 Mybatis 所有的配置信息,包括mybatis配置文件、EmployeeMapper.xml等文件都会被预先加载到Configuration里。

这时候,我们发现Configuration里面出现了一个mapperRegistry,这个是mapper的注册器,其实在加载EmployeeMapper.xml的时候,我们就需要在mapperRegistry里面进行注册,所以,我们可以从这里进行获取。继续走~
(3)进入到mapperRegistry的getMapper方法

这里分为了两步执行:
(1)knownMappers.get(type);
获取已知的加载过的mapper中获取出mapper代理工厂
(2)mapperProxyFactory.newInstance(sqlSession);
代理工厂生成动态代理返回
那么knownMappers其实是个map集合,根据EmployeeMapper.class获取MapperProxyFactory,所以knownMappers必然是源码前面的步骤中set进去的。我们先找找,到底是哪里set进去的呢,继续调试我们发现XMLMapperBuilder类的bindMapperForNamespace方法

这个方法里面的boundType,是通过Resources.classForName(namespace);生成的class,Resources.classForName底层其实就是调用Class.forName生成的反射对象,而参数是namespace,namespacne就是com.mybatis3.mappers.EmployeeMapper:
Class.forName(com.mybatis3.mappers.EmployeeMapper)生成反射对象。生成的boundType在被configuration.addMapper(boundType);可以看到在这里调用了put方法放进去的。


(4) 接下来再回到mapperRegistry的getMapper方法继续往下调试进入到MapperProxyFactory的newInstance里面,看到该方法return newInstance(mapperProxy)回来,继续往下调试

(5)进入到newInstance这个方法里面我们看到了Proxy.newProxyInstance代码,就是JDK创建代理的方法

到这里我们发现是调用了JDK动态代理的newProxyInstance方法。
(6)那么我们来看看是如何代理的,打开MapperProxy这个类,找到里面的invoke方法

在这个方法里面MapperMethod 类有两个重点,其一就是初始化,其二就是执行SQL,为什么说初始化很重要呢?因为初始化的时候需要通过接口名全称+方法全称去 Configuration 找我们之前加载的SQL,这也就是为什么接口方法定义和SQL的ID必须保持一致的原因;其二执行SQL。
(7)接着MapperMethod 调用 execute 执行 SQL,代码很简单如下



在Case:SELECT下 我们找到
result = this.executeForMany(sqlSession, args);
(8)我们点开executeForMany方法

最终我们发现是调用了sqlSession.selectList方法,然后根据方法名找到EmployeeMapper.xml文件中对应id标识,执行sql语句完成了查询操作。
总结
今天我们从认为Mapper接口是创建出来的代理对象完成了查询操作出发,一步步进行了调试发现,最终Mybatis的接口不需要实现类,实际上是使用了JDK动态代理的方式创建代理对象完成的。
回复

使用道具 举报

网站地图|页面地图|文字地图|Archiver|手机版|小黑屋|找资源 |网站地图

GMT+8, 2024-4-30 23:25

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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