关注“Java 后端技术全栈”
回复“面试”获取全套面试材料
背景
前段时间在我的技术群里,大家探讨起了为什么 UserMapper.java
是个接口,没有具体实现类,而咱们能够间接调用其办法?
对于这个问题,我之前面试过一些人,很多人是这么答复的:
1. 我领导叫咱们应用
Mybatis
,大家都这么用就这么用了(没想过,反正就这么用)。2. 尽管我不晓得具体是怎么实现的,但我感觉必定是……(此处略去若干的漫天猜测),然而也不对啊,难道是……(再次略去若干似懂非懂)。
3. 应用动静代理实现的(而后就没有下文了)。
对于下面的三种答复,后面两种咱们就没必要往下聊了。
然而第三种答复,就有必要往下问:那你说说动静代理有哪些实现形式?Mybatis
应用的是哪一种?
如果这个问题你还能答复上来,那么还会持续问:UserMapper.java
中大办法能不能重载?
如果你能答复下面的问题,本文就没必要往下看了,曾经不适宜你了。
问题剖析
先来看一张图,这图里的代码就是咱们后面写的 demo:
为什么一个接口就能和一个 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/act…
其实,又差不多回到了 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
中的这个流程。
接口 Mapper 内的办法能重载吗?
相似上面:
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 中的办法是相对不能重载的。
举荐浏览
把握 Mybatis 动静映射,我可是下了功夫的
《写给大忙人看的 JAVA 核心技术》.pdf