MyBatis的底层实现原理
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
<div class="htmledit_views" id="content_views">
<p><strong>动态代理的功能:通过拦截器方法回调,对目标target方法进行增强。</strong></p>
言外之意就是为了增强目标target方法。上面这句话没错,但也不要认为它就是真理,殊不知,动态代理还有投鞭断流的霸权,连目标target都不要的科幻模式。
注:本文默认认为,读者对动态代理的原理是理解的,如果不明白target的含义,难以看懂本篇文章,建议先理解动态代理。
一、自定义JDK动态代理之投鞭断流实现自动映射器Mapper
首先定义一个pojo
再定义一个接口UserMapper.java
接下来我们看看如何使用动态代理之投鞭断流,实现实例化接口并调用接口方法返回数据的。
自定义一个InvocationHandler。
上面代码中的target,在执行Object.java内的方法时,target被指向了this,target已经变成了傀儡、象征、占位符。在投鞭断流式的拦截时,已经没有了target。
写一个测试代码:
output:
这便是Mybatis自动映射器Mapper的底层实现原理。
可能有读者不禁要问:你怎么把代码写的像初学者写的一样?没有结构,且缺乏美感。
必须声明,作为一名经验老道的高手,能把程序写的像初学者写的一样,那必定是高手中的高手。这样可以让初学者感觉到亲切,舒服,符合自己的Style,让他们或她们,感觉到大牛写的代码也不过如此,自己甚至写的比这些大牛写的还要好,从此自信满满,热情高涨,认为与大牛之间的差距,仅剩下三分钟。
二、Mybatis自动映射器Mapper的源码分析
首先编写一个测试类:
Mapper长这个样子:
org.apache.ibatis.binding.MapperProxy.java部分源码。
org.apache.ibatis.binding.MapperProxyFactory.java部分源码。
这便是Mybatis使用动态代理之投鞭断流。
三、接口Mapper内的方法能重载(overLoad)吗?(重要)
类似下面:
Answer:不能。
原因:在投鞭断流时,Mybatis使用package+Mapper+method全限名作为key,去xml内寻找唯一sql来执行的。类似:key=x.y.UserMapper.getUserById,那么,重载方法时将导致矛盾。对于Mapper接口,Mybatis禁止方法重载(overLoad)。
深入MyBatis-运行原理-getMapper获取到接口的代理对象
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
<div id="content_views" class="markdown_views prism-atom-one-dark">
<!-- flowchart 箭头图标 勿删 -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
<h4><a id="_0"></a>流程图</h4>
- DefaultSqlSessionF调用getMapper方法,其中为configuration下的getMapper方法
- configuration下的getMapper方法,其中为mapperRegistry下的getMapper方法
- mapperRegistry下的getMapper方法下根据接口类型获取MapperProxyFactory
- MapperProxyFactory调用newInstance生成MapperProxy
- 创建MapperProxy的代理对象,一路返回到最初调用的DefaultSqlSession
剖析源码
1.DefaultSqlSessionF调用getMapper方法
//传入接口类
sqlSession.getMapper(StudentMapper.class);
- 1
- 2
2.调用configutaion的getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
- 1
- 2
- 3
3.调用mapperRegistry的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
- 1
- 2
- 3
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//根据接口类型生成代理类工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//此处将返回MapperProxy的代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4.创建MapperProxy代理对象
public T newInstance(SqlSession sqlSession) {
//生成MapperProxy
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
//生成MapperProxy的代理对象,之后一路返回
return newInstance(mapperProxy);
}
- 1
- 2
- 3
- 4
- 5
- 6
小结
DefaultSqlSession下调用getMapper方法,最终根据传入接口的类型生成MapperProxy工厂,创建MapperProxy,最终返回MapperProxy的代理对象。
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
</div>
</article>
<div class="postTime">
<div class="article-bar-bottom">
<span class="time">
文章最后发布于: 2019-03-15 09:42:06 </span>
</div>
</div>
2. mybatis为什么直接调用接口方法就可以实现语句的查询
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
<div id="content_views" class="markdown_views prism-atom-one-dark">
<!-- flowchart 箭头图标 勿删 -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
<p>上一篇文章我们了解了什么是mybatis,并通过一个用例实现了一个简单的mybatis查询数据库的数据,在文章的最后针对mybatis如何实现这些功能提出了几个问题。接下来的文章中会对这些问题做一一解答。<br>
本篇文章中解决第一个问题,mybatis为什么调用接口方法就可以实现语句的查询?
实现该功能主要分成两块进行:
- 注册Mapper
- 通过new SqlSessionFactoryBuilder().build(inputStream);构建SqlSessionFactory。在builder方法中将配置文件中的内容转换为Configuration中的对象属性。SqlSessionFactory持有Configuration对象的引用。
- 获取Mapper,调用接口方法
mybatis查询通过以下几个关键类和代码实现
- 通过sqlSession = factory.openSession();创建SqlSession对象
- 通过TestMapper testMapper = sqlSession.getMapper(TestMapper.class);获取mapper代理对象
- 通过testMapper.queryTestDO(1));获取数据库结果集
注册部分关键代码:
XmlConfigBuilder.java
private void