细说Mybatis一级缓存、二级缓存以及mybatis获取mapper的面向接口编程思想(Mapper接口动态代理实现原理)(二)
上一章和大家分享了Mybatis一级缓存和二级缓存,本章将继续和大家分享Mapper接口动态代理实现原理,按照国际惯例,先看源码,然后结合原理,写一个自己的小demo,从理论到实战,真正掌握面向接口编程的思想。
还是使用上一章的工程,这里就不占用篇幅了,可以访问下面的网址访问。
https://blog.csdn.net/CSDN_LICY/article/details/108764141
一、原理篇
(首先说明下我的idea快捷方式设置的是和eclipse一样,F5步进,F6下一步,F7步出,F8到下一断点。)
首先从Mybatis加载config.xml配置文件并解析mapper开始讲解。
private void bindMapperForNamespace() {//获取namespace,xml里面的namespace一定要和mapper接口的全路径名一致String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {//interface com.study.mybatisdemo.mapper.CountryMapperboundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {//ignore, bound type is not required}if (boundType != null) {if (!configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);//将CountryMapper添加到configurationconfiguration.addMapper(boundType);}}}}
此时位于MapperRegistry的addMapper方法,
public <T> void addMapper(Class<T> type) {//必须是接口类型,因为使用的是jdk动态代理if (type.isInterface()) {//如果knownMappers已经存在了此类型的MapperProxyFactory,则抛出异常。if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {//首先根据Type构建一个MapperProxyFactory,并按照type放到knownMappersknownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
到此,已经将Country的工厂类放到了knownMappers,接下来看是如何获取的mapper
此时位于MapperRegistry的getMapper方法,可以看到从knownMappers取出来了mapperProxyFactory,然后利用mapperProxyFactory.newInstance获取一个实例,继续走,看newInstance怎么做的。
此时位于MapperProxyFactory的newInstance方法,将sqlSession传递进去构建一个MapperProxy。
此时此时位于MapperProxyFactory的newInstance方法,可以看到使用Proxy返回一个代理对象。
再次回到测试案例,发现此时countryMapper就是一个代理对象。那这个代理对象是怎么执行的呢?继续往下走。
MapperProxy实现了InvocationHandler,因此走到invoke方法,首先获取PlainMethodInvoker,然后调用PlainMethodInvoker的invoke方法。
PlainMethodInvoker的invoke方法如下:
最终来到MapperMethod的execute方法,可以看到此时已经将mapper方法转换成了sqlSession方法的调用。此时的command.getName()返回的是com.study.mybatisdemo.mapper.CountryMapper.selectById,就是XML文件中namespace和具体方法id的组合。
总结如下:当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到一个方法id,这个id就是映射xml中namespace和具体方法id的组合。所以可以在代理方法中使用sqlsession以命名空间的方式调用方法。通过这种方式可以将接口和XML文件中的方法关联起来。这种代理方式和常规的不同之处在于,这里没有对某个具体类进行代理,而是通过代理转化成了对其他代码的调用。
哈哈,通过上面的原理讲解,大家是不是对动态代理更加感兴趣了,下面通过一个简单的例子,来实战一下。
二、实战篇
(1)首先准备一个接口,用于模仿mapper
public interface Sell {public void sellSomeThing(String kind);public void sellOnline();
}
(2)准备两个实现类,实现sell接口,这是常规的动态代理实现
public class SellApple implements Sell{public SellApple() {System.out.println("苹果商铺");}@Overridepublic void sellSomeThing(String kind) {System.out.println("开始卖苹果,种类:" + kind + "=========");}@Overridepublic void sellOnline() {}
}
public class SellJuzhi implements Sell{public SellJuzhi() {System.out.println("橘子商铺");}@Overridepublic void sellSomeThing(String kind) {System.out.println("开始卖橘子,种类:" + kind + "=========");}@Overridepublic void sellOnline() {}
}
(3)准备一个普通类,用于模仿sqlsession,通过动态代理这个桥梁将对接口方法的调用转换为对其他方法的调用
public class OnLineShopping {public String sellOnLine() {System.out.println("我是电子商铺,不需要店铺,哈哈哈");return "电子商铺";}}
(4)准备一个普通的动态代理类增强器InvocationHandler
public class MyInvocationHandler implements InvocationHandler {private Sell sell;public MyInvocationHandler(Sell sell) {this.sell = sell;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开始卖东西喽。。。。。。。。。。。");Object invoke = method.invoke(sell, args);System.out.println("卖完了,好开心。。。。。");return invoke;}}
(5)准备一个MybatisInvocationHandler 用于模仿mybatis的mapper调用流程
public class MybatisInvocationHandler implements InvocationHandler {private OnLineShopping onLineShopping;public MybatisInvocationHandler(OnLineShopping onLineShopping) {this.onLineShopping = onLineShopping;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//将sell接口中sellOnLine的方法,转换为onLineShopping中sellOnLine方法的调用return onLineShopping.sellOnLine();}}
(6)主测试案例
public class SellMain {public static void main(String[] args) {//这是将代理类生成到本地,可以通过看代理类源码的方式,更加了解代理类是如何执行的。System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//普通的代理类,是有实现类的Sell sellApple = (Sell) Proxy.newProxyInstance(SellApple.class.getClassLoader(), SellApple.class.getInterfaces(), new MyInvocationHandler(new SellApple()));Sell sellJuzhi = (Sell) Proxy.newProxyInstance(SellJuzhi.class.getClassLoader(), SellJuzhi.class.getInterfaces(), new MyInvocationHandler(new SellJuzhi()));sellApple.sellSomeThing("红富士");System.out.println("--------------------------------------------");sellJuzhi.sellSomeThing("贡菊");System.out.println("---------------------面向接口编程-----------------------");//将sell的sellOnline转换为OnLineShopping的sellOnlineSell sell = (Sell) Proxy.newProxyInstance(OnLineShopping.class.getClassLoader(), new Class[] { Sell.class }, new MybatisInvocationHandler(new OnLineShopping()));sell.sellOnline();}}
结果如下:
至此细说Mybatis一级缓存、二级缓存以及mybatis获取mapper的面向接口编程思想(Mapper接口动态代理实现原理)终于讲完了,感谢大家的阅读,有问题欢迎留言,指正。
路漫漫其修远兮,吾将上下而求索
细说Mybatis一级缓存、二级缓存以及mybatis获取mapper的面向接口编程思想(Mapper接口动态代理实现原理)(二)相关推荐
- Mybatis3.4.x技术内幕(二十二):Mybatis一级、二级缓存原理分析
2019独角兽企业重金招聘Python工程师标准>>> Mybatis的一级缓存,指的是SqlSession级别的缓存,默认开启:Mybatis的二级缓存,指的是SqlSession ...
- mybatis一级,二级缓存。缓存带来的脏读问题
title 1. 关于缓存的介绍 2. 一级缓存,默认开启,session级别 3. 二级缓存,mapper 的namespace级别 1. 关于缓存的介绍 Mybatis一级缓存的作用域是同一个Sq ...
- 深入浅出 MyBatis 的一级、二级缓存机制
一.MyBatis 缓存 缓存就是内存中的数据,常常来自对数据库查询结果的保存.使用缓存,我们可以避免频繁与数据库进行交互,从而提高响应速度. MyBatis 也提供了对缓存的支持,分为一级缓存和二级 ...
- mybatis教程--查询缓存(一级缓存二级缓存和整合ehcache)
查询缓存 1 缓存的意义 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题. 2 mybat ...
- MyBatis 一级缓存二级缓存详解
相关内容: 架构师系列内容:架构师学习笔记(持续更新) MyBatis 缓存详解 cache 缓存 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力.跟Hibernat ...
- 【mybatis】Mybatis中的一级、二级缓存
[mybatis]简介 [mybatis]mybatis & mybatis-plus & hibernate的区别 [mybatis]核心成员分析 [mybatis]Mybatis的 ...
- 【MyBatis学习13】MyBatis中的二级缓存
1. 二级缓存的原理 前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的.为了更加 ...
- MyBatis中的二级缓存
MyBatis中的二级缓存 1. 二级缓存的原理 前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二 ...
- MyBatis学习系列——二级缓存
[MyBatis学习13]MyBatis中的二级缓存 发表于2016/6/16 7:26:19 4922人阅读 分类: ● 框架技术 --[MyBatis] 1. 二级缓存的原理 前面介绍了,myb ...
最新文章
- ASP.NET中实现大结果集分页研讨 转
- poj3624 Charm Bracelet DP 01背包问题
- 李佳琦一晚卖了100亿,有位“硬汉”在背后默默发力
- 机器学习入门学习笔记:(4.2)SVM的核函数和软间隔
- wxWidgets:工具栏概述
- 【TensorFlow-windows】学习笔记六——变分自编码器
- (软件工程复习核心重点)第二章可行性研究习题
- 跨屏html ui,Amaze UI(HTML5 跨屏前端框架) v2.7.2
- 老外写的比较好用的splitter控件
- C语言中数字转换成字符,c语言中数字转换成字符串的方法
- 游戏使用html签名,利用HTML5实现电子签名板文字涂鸦代码
- 用html代码在word中插入分页符,Word分页符怎么用?Word插入分页符的方法
- 2007年“网络十大炒女”排行榜
- Windows Xp SP3 chs 简体中文版下载
- Linux 任务计划的三种实现方式(at、batch、cron)
- 2021-07-16思考-资本源于贪婪(与人性抗争)
- 2021年10月程序员薪资出炉,你在哪个层级?
- 遥感影像如何导入Photoshop软件进行PS调色-智拼图Photoshop交互工具使用技巧
- 解决Android11 无法访问/Android/data文件夹的问题
- openlayers离线文档_openlayers学习