通过模拟Mybatis动态代理生成Mapper代理类,讲解Mybatis核心原理
本文将通过模拟Mybatis动态代理生成Mapper代理类,讲解Mybatis原理
1.平常我们是如何使用Mapper的
先写一个简单的UserMapper,它包含一个全表查询的方法,代码如下
public interface UserMapper {
@Select("select * from user")
public List<User> queryAll();
}
然后大家思考一个问题,我们平时是怎么使用这个UserMapper的?
很多时候我们会把Mybatis和Spring整合起来一起使用,于是会有类似下面的代码:
@Service
public class UserServiceImpl {
@Autowired
private UserMapper userMapper;
public List<User> queryAll(){
return this.userMapper.queryAll();
}
}
看到这段熟得不能再熟的代码不知道大家会不会有一丝疑惑:UserMapper明明是一个接口,为什么可以直接调用他的queryAll方法呢?
这个问题其实也不难解,我们不能直接调用一个接口的方法,这背后肯定是有一个对象的,至于这个对象是怎么来的,这里直接告诉大家是通过动态代理生成的。只要弄懂了这个动态代理对象是怎么生成的,整个Mybatis框架原理就就说清楚了。在模拟之前我们先验证一下是不是使用JDK的动态代理。
2.验证Mybatis是通过动态代理生成Mapper代理对象
由于上面一段代码整合了spring,spring又为我们封装了许多细节,我们重新看一段代码,看看没有spring的情况下我们怎么获得一个UserMapper
public static void main(String[] args){
SqlSessionFactory sqlSessionFactory = .... //这里省略,官网给了很多配置SqlSessionFactory的方法(不一定是这么获得)
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
}
为了验证获得UserMapper采用的是动态代理,我们可以在IDE中对着session.getMapper(UserMapper.class)一路按着Ctrl点进去,我们会发现最终调用的代码是这样的:
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
最后果然调用的是JDK的动态代理
3.动手模拟一次Mybatis的动态代理
既然知道了原理,下面我们就动手验证吧!
为了简单明了,我们不写SqlSessionFactory类了,直接自定义一个MySession类,在里面给出模拟的getMapper方法:
public class MySession {
public static Object getMapper(Class clazz){
//调用newProxyInstance需要传入class数组
Class[] clazzs = new Class[]{clazz};
//把动态代理过程中生成的代理类保留下来,有助于新手理解动态代理(此行可省略)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//通过动态代理生成Mapper
Object object = Proxy.newProxyInstance(MySession.class.getClassLoader(), clazzs, new MyInvocationHandler());
return object;
}
}
如果不熟悉JDK的动态代理也没关系,下面我会逐步分析。
大家可以看到调用动态代理生成动态代理对象需要三个参数:
- 类加载器
- class数组
- InvocationHandler实例
为什么需要类加载器?
动态代理生成类和其调用类必须通过同一个类加载器加载,否则它们之间无法相互调用
为什么有个class数组?
JDK的动态代理是基于接口的,class数组中存放的是动态代理类需要实现的接口。比如本文中的例子生成的动态代理类需要实现UserMapper接口,所以你得把接口告诉它。
为什么会有InvocationHandler实例?
动态代理会在原有方法上实现增强,而增强的逻辑就写在InvocationHandler类的invoke方法上,所以要有这么个实例。
你想一想,当初的这段代码
public interface UserMapper {
@Select("select * from user")
public List<User> queryAll();
}
,我们想实现一个怎样的功能?
无非就是给它一条sql语句,希望它能去数据库中执行这条sql语句并返回结果。这个过程可以拆分成两个部分:
- 得到sql语句
- 通过JDBC操作数据库,并执行sql返回结果
这里省略JDBC的过程,给出一个简单的invoke方法示例(Mybatis为我们封装了一切JDBC的处理细节):
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//解析得到sql
Select annotation = method.getAnnotation(Select.class);
String sql = annotation.value()[0];
//执行sql(模拟JDBC)
System.out.println(sql + " executing...");
return null;
}
}
最终我们执行UserMapper的queryAll()方法时,就会出现如下结果:
public class Main {
public static void main(String[] args) {
UserMapper userMapper = (UserMapper) MySession.getMapper(UserMapper.class);
userMapper.query();
}
}
//打印:
//select * from user executing...
总结
最后我们总结一下Mybatis框架的核心原理:
- 用户只需要创建Mapper接口,并使用Mapper接口即可。
- Mybatis会对Mapper接口产生动态代理对象,这个动态代理对象实现了Mapper接口,拥有Mapper中定义的所有方法,并对这些方法进行了增强。增强的逻辑是获得sql语句和执行sql语句。
通过个核心原理我们也就知道了Mybatis为我们做了什么:
- 让方法和sql语句对应起来,操作数据库就如同调用方法一般简单
- 屏蔽掉JDBC的细节
通过模拟Mybatis动态代理生成Mapper代理类,讲解Mybatis核心原理的更多相关文章
- Mybatis源码解析 - mapper代理对象的生成,你有想过吗
前言 开心一刻 本人幼教老师,冬天戴帽子进教室,被小朋友看到,这时候,有个小家伙对我说:老师你的帽子太丑,赶紧摘了吧.我逗他:那你好好学习,以后给老师买个漂亮的?这孩子想都没想立刻回答:等我赚钱了,带 ...
- 【MyBatis学习04】mapper代理方法开发dao
上一篇博文总结了mybatis使用 原始dao的方法存在的一些弊端,我们肯定不会去用它,那么mybatis中该如何开发dao呢?如题所述,这篇博文主要来总结一下使用mapper代理的方法来开发dao的 ...
- MyBatis入门程序之Mapper代理方式
Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可,MyBatis会自动为mapper接口生成动态代理实现类. 一.开发规范 1.mapper接口的全限定名要和map ...
- mybatis系列笔记(2)---mapper代理方法
mapper代理方法 在我们在写MVC设计的时候,都会写dao层和daoimp实现层,但假如我们使用mapper代理的方法,我们就可以不用先daoimp实现类 当然这得需要遵守一些相应的规则: (1) ...
- 如果使用mybatis的逆向工程生成的po类及mapper,如果我们想要进行的对数据库的操作在mapper中没有对应的接口函数:比如生成的mapper接口中没有按照姓名及性别混合条件查询。我们的解决办法是:使用逆向工程生成的对应表的Example文件。
1.使用mybatis逆向工程生成的po类中包含UserExample文件(我的数据库表名为User). 2. 创建UserExample对象,然后对加入条件.对应的测试代码为: /* * 通过姓名和 ...
- Mybatis+Spring整合后Mapper测试类编写
public class UserMapperTest { private ApplicationContext applicationContext; @Before public void ini ...
- java-jdk动态代理生成的代理类源码
import com.zkn.newlearn.gof.proxyhandler.PersonInter; import java.lang.reflect.InvocationHandler; im ...
- mybatis自己主动生成mapper,dao,映射文件
一.先创建数据脚本,这里用的mysql数据脚本 drop table VOTE_ITEM; drop table VOTE_OPTION; drop table VOTE_SUBJECT; drop ...
- mybatis由浅入深day01_5.3 Mapper动态代理方法
5.3 Mapper动态代理方法(程序员只需要写mapper接口(相当于dao接口)) 5.3.1 实现原理(mapper代理开发规范) 程序员还需要编写mapper.xml映射文件 程序员编写map ...
随机推荐
- HDU 2674
0 <= N<=10^9 看到这个数据范围知道常规方法肯定做不出来. 不过一想想既然是mod2009,是不是只要其中含有一个2009,那么其结果一定是0了呢 说了这里思路,就是看什么时候出 ...
- dotnet 设计规范 · 抽象类
X 不要定义 public 或 protected internal 访问的构造函数.默认 C# 语言不提供抽象类的公开构造函数方法. 如果一个构造函数定义为公开,只有在开发者需要创建这个类的实例的时 ...
- springboot2.0.2+redis+spring-session 解决session共享的问题
准备工作 新建两个springboot2.0.2版本的服务,配置文件添加: #在默认设置下,Eureka服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为 eurek ...
- Anagram——[枚举全排列]
预备知识: 1.求0—n个数全排列的算法: void print_permutation(int n,int *A,int cur){ if(cur==n){ ;i<cur;i++) cout& ...
- WPF 解决弹出模态窗口关闭后,主窗口不在最前
本文告诉大家如何解决这个问题,在 WPF 的软件,弹出一个模态窗口.使用另一个窗口在模态窗口前面.从任务栏打开模态窗口.关闭模态窗口.这时发现,主窗口会在刚才使用的另一个窗口下面 这是 Windows ...
- artDialog4.0.5
artDialog4.0.5 2011-08-22 11:54:36 haiwei_sun 阅读数 4109 收藏 更多 分类专栏: jquery 下载 Google Code 项目主页 最新版 ...
- 2018百度之星资格赛A B F
A.调查问卷 度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 mm 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项. 将问卷散发出去之后,度度熊收到了 nn ...
- C# 判断系统版本
本文告诉大家如何判断系统是 win7 还是 xp 系统 使用下面代码可以判断 private static readonly Version _osVersion = Environment.OSVe ...
- js算法(2)
1寻找一个数组中最多的那个数 (1)利用数组 function findMostNum(arr){ var temp1=[];//存放去重的数字 var temp2=[];//存放各个数字的个数 va ...
- Consul etcd ZooKeeper euerka 对比
这里就平时经常用到的服务发现的产品进行下特性的对比,首先看下结论: Feature Consul zookeeper etcd euerka 服务健康检查 服务状态,内存,硬盘等 (弱)长连接,kee ...