本文将通过模拟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的动态代理也没关系,下面我会逐步分析。

大家可以看到调用动态代理生成动态代理对象需要三个参数:

  1. 类加载器
  2. class数组
  3. InvocationHandler实例

为什么需要类加载器?

动态代理生成类和其调用类必须通过同一个类加载器加载,否则它们之间无法相互调用

为什么有个class数组?

JDK的动态代理是基于接口的,class数组中存放的是动态代理类需要实现的接口。比如本文中的例子生成的动态代理类需要实现UserMapper接口,所以你得把接口告诉它。

为什么会有InvocationHandler实例?

动态代理会在原有方法上实现增强,而增强的逻辑就写在InvocationHandler类的invoke方法上,所以要有这么个实例。

你想一想,当初的这段代码

public interface UserMapper {

    @Select("select * from user")
public List<User> queryAll();
}

,我们想实现一个怎样的功能?

无非就是给它一条sql语句,希望它能去数据库中执行这条sql语句并返回结果。这个过程可以拆分成两个部分:

  1. 得到sql语句
  2. 通过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框架的核心原理:

  1. 用户只需要创建Mapper接口,并使用Mapper接口即可。
  2. Mybatis会对Mapper接口产生动态代理对象,这个动态代理对象实现了Mapper接口,拥有Mapper中定义的所有方法,并对这些方法进行了增强。增强的逻辑是获得sql语句和执行sql语句。

通过个核心原理我们也就知道了Mybatis为我们做了什么:

  1. 让方法和sql语句对应起来,操作数据库就如同调用方法一般简单
  2. 屏蔽掉JDBC的细节

通过模拟Mybatis动态代理生成Mapper代理类,讲解Mybatis核心原理的更多相关文章

  1. Mybatis源码解析 - mapper代理对象的生成,你有想过吗

    前言 开心一刻 本人幼教老师,冬天戴帽子进教室,被小朋友看到,这时候,有个小家伙对我说:老师你的帽子太丑,赶紧摘了吧.我逗他:那你好好学习,以后给老师买个漂亮的?这孩子想都没想立刻回答:等我赚钱了,带 ...

  2. 【MyBatis学习04】mapper代理方法开发dao

    上一篇博文总结了mybatis使用 原始dao的方法存在的一些弊端,我们肯定不会去用它,那么mybatis中该如何开发dao呢?如题所述,这篇博文主要来总结一下使用mapper代理的方法来开发dao的 ...

  3. MyBatis入门程序之Mapper代理方式

    Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可,MyBatis会自动为mapper接口生成动态代理实现类. 一.开发规范 1.mapper接口的全限定名要和map ...

  4. mybatis系列笔记(2)---mapper代理方法

    mapper代理方法 在我们在写MVC设计的时候,都会写dao层和daoimp实现层,但假如我们使用mapper代理的方法,我们就可以不用先daoimp实现类 当然这得需要遵守一些相应的规则: (1) ...

  5. 如果使用mybatis的逆向工程生成的po类及mapper,如果我们想要进行的对数据库的操作在mapper中没有对应的接口函数:比如生成的mapper接口中没有按照姓名及性别混合条件查询。我们的解决办法是:使用逆向工程生成的对应表的Example文件。

    1.使用mybatis逆向工程生成的po类中包含UserExample文件(我的数据库表名为User). 2. 创建UserExample对象,然后对加入条件.对应的测试代码为: /* * 通过姓名和 ...

  6. Mybatis+Spring整合后Mapper测试类编写

    public class UserMapperTest { private ApplicationContext applicationContext; @Before public void ini ...

  7. java-jdk动态代理生成的代理类源码

    import com.zkn.newlearn.gof.proxyhandler.PersonInter; import java.lang.reflect.InvocationHandler; im ...

  8. mybatis自己主动生成mapper,dao,映射文件

    一.先创建数据脚本,这里用的mysql数据脚本 drop table VOTE_ITEM; drop table VOTE_OPTION; drop table VOTE_SUBJECT; drop ...

  9. mybatis由浅入深day01_5.3 Mapper动态代理方法

    5.3 Mapper动态代理方法(程序员只需要写mapper接口(相当于dao接口)) 5.3.1 实现原理(mapper代理开发规范) 程序员还需要编写mapper.xml映射文件 程序员编写map ...

随机推荐

  1. Vue 属性绑定v-bing 事件绑定v-on

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 从http到https--phpStudy2018

    0. 将SSL证书解压到以下目录,申请方式见 百度 Apache/cert/ 分别更名为 my_public.crt my.key my_chain.crt 1. phpStudy->其它选项菜 ...

  3. HDU 5912 Fraction(模拟)

    Problem Description Mr. Frog recently studied how to add two fractions up, and he came up with an ev ...

  4. Linux 基础(一)stat函数

    Header file: #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> DEFI ...

  5. visual studio 2010问题修复

    我在重新安装 Visual Studio 2010 和 SQL sever 2012 的时候,安装好的两个软件打开时都遇到了这个问题:“在此计算机中仅有部分 Microsoft Visual Stud ...

  6. phpcms 增加备案号、联系方式等字段

    准备好记事本或者dreamweaver或者其它文本编辑器 打开\phpcms\languages\zh-cn\admin.lang.php PHPCMS的中文语言定义文件. 查找“site_manag ...

  7. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  8. DataBinding + Kotlin +Viewpager

    1.创建viewmodel,其中BindAdapter的方法需要是静态方法,因此需要加@JvmStatic,"app:img"相当于一个自定义属性,后面xml中会用到,当app:i ...

  9. 【网摘】一个用shell写的俄罗斯方块

    一位大神的shell,值得学习:https://blog.csdn.net/zhenliang8/article/details/85316926 详见下: #!/bin/bash APP_NAME= ...

  10. Python上下文管理使用

    import contextlib from queue import Queue @contextlib.contextmanager def myOpen(file): f = open(file ...