关注“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