前段时间在个人技术群里,你们讨论起了为何UserMapper.java
是个接口,没有具体实现类,而咱们能够直接调用其方法?java
关于这个问题,我以前面试过一些人,不少人是这么回答的:git
1.我领导叫咱们使用
Mybatis
,你们都这么用就这么用了(没想过,反正就这么用)。面试2.虽然我不知道具体是怎么实现的,但我以为确定是……(此处略去若干的漫天猜测),可是也不对啊,难道是……(再次略去若干似懂非懂)。sql
3.使用动态代理实现的(而后就没有下文了)。apache
对于上面的三种回答,前面两种咱们就不必往下聊了。mybatis
可是第三种回答,就有必要往下问:那你说说动态代理有哪些实现方式?Mybatis
使用的是哪种?app
若是这个问题你还能回答上来,那么还会继续问:UserMapper.java
中大方法能不能重载?ide
若是你能回答上面的问题,本文就不必往下看了,已经不适合你了。测试
先来看一张图,这图里的代码就是咱们前面写的demo:this
为何一个接口就能和一个xml文件给绑定的呢?这就是今天咱们要聊的话题。
可能不少小伙伴不熟悉ibatis,2010年以前,尚未Mybatis
,以后ibatis便成了如今的Mybatis
,若是有兴趣的朋友,能够看到Mybatis
中的包目录。
这个包目录中就仍是ibatis,而且ibatis的做者如今就在腾讯上班,开发英雄联盟LOL。
若是有腾讯的小伙伴能够打听打听哈,大佬就在身边。言归正传。
Mapper层在
Mybatis
中如今是接口形式就搞定了,而在ibatis时代仍是必需要有实现类的,我记得2012年的时候,使用的就是ibatis,Dao(Mapper)必需要有实现类。
下面咱们就来看看Mybatis
中是怎么作的。
继续使用咱们上一节中的代码。
controller
service实现类中
打一个断点,而后使用debug模式启动项目。并访问:
http://localhost:9002/test
userMapper=org.apache.ibatis.binding.MapperProxy@6da21078
发现Mybatis
给UserMapper.java
生成了一个代理对象,而且从名字上能够看出是JDK动态代理。
关于动态代理请,这里我推荐我以前写过的一篇文章:
https://gitbook.cn/m/mazi/activity/5d44e35e4fbf44126135c292?sut=c93c00a03b4f11eba07ad99b4dfbdab0&utm_source=chatweixinshare
其实,又差很少回到了ibatis时代,只是Mybatis中是经过动态代理的方式生成的代理类不是咱们开发的,而是经过JDK动态代理生成的代理类。
下面咱们也使用JDK动态代理来模拟一把。
public class MapperProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
// 诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
return method.invoke(this, args);
} catch (Throwable t) {
}
}
// 投鞭断流
return new User((Integer) args[0], "田维常", 22);
}
}
再写一个测试类
import com.tian.mybatis.entity.User;
import com.tian.mybatis.mapper.UserMapper;
public class TestProxy {
public static void main(String[] args) {
MapperProxy proxy = new MapperProxy();
UserMapper mapper = proxy.newInstance(UserMapper.class);
User user = mapper.selectById(999);
System.out.println(user);
System.out.println(mapper.toString());
}
}
输出
User{id=999, userName='田维常', age=22, gender=null}
com.tian.mybatis.proxy.MapperProxy@39a054a5
这即是Mybatis
自动映射器Mapper的底层实现原理。
可是在Mybatis
中,远远不是这么简单的,可是本质就是这样的。
下面咱们就来大体分析一下Mybatis
中的这个流程。
相似下面:
public User getUserById(Integer id);
public User getUserById(Integer id, String name);
答案:不能
由于Mybatis
中是使用package+Mapper+method全限名做为key,去xml内寻找惟一sql来执行的。
相似:key=com.tian.mybatis.UserMapper.getUserById
,那么,重载方法时将致使矛盾。
对于Mapper接口,Mybatis
禁止方法重载(overLoad) 。
在MapperMethod类的静态内部类中SqlCommand中有个resolveMappedStatement方法。
在Configuration中有个属性,就是项目启动的时候,会把Mapper.xml中信息解析到这个属性里,以咱们指定的namespace+method做为key放到Map里面,后面咱们调用Mapper接口动态类的某个方法时候再去map获取。
protected final Map<String, MappedStatement> mappedStatements
就是使用类的全路径名.方法做为key存放到Map中的。
经常使用动态代理方式:JDK动态代理和CGlib动态代理。
Mybatis是采用JDK动态代理+反射+xml来解决接口绑定的,为咱们建立能够调用的代理对象。
咱们的Mapper中的方法是绝对不能重载的。