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

发现MybatisUserMapper.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