本文将通过模拟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. Python--day66--内容回顾

    3,python中的大小比较和js中的大小比较规则: python中a>b>c,就是先比较a>b,然后再比较b>c,都为true的话就返回true: js中的a>b> ...

  2. Python--day29--configparser模块(配置)(不熟,以后要找时间重学)

  3. 2018-2-13-C#-枚举转字符串

    title author date CreateTime categories C# 枚举转字符串 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17:23:3 ...

  4. java数组简介

    数组(Array)是Java 语言中内置的一种基本数据存储结构,通俗的理解,就是一组数的集合,目的是用来一次存储多个数据.数组是程序中实现很多算法的基础,可以在一定程度上简化代码的书写. 备注: 数组 ...

  5. IP地址和物理地址有什么区别

    所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址.简单地说就是你在整个互联网上的ID. MAC(Media Access Control,介质访问控制)地址 (物理地址)是 ...

  6. H3C RIPv2配置任务

  7. Linux 内核 低级 sysfs 操作

    kobject 是在 sysfs 虚拟文件系统之后的机制. 对每个在 sysfs 中发现的目录, 有一个 kobject 潜伏在内核某处. 每个感兴趣的 kobject 也输出一个或多个属性, 它出现 ...

  8. Linux 内核 回顾: ISA

    设计上 ISA 总线非常老了, 并且是非常地低能, 但是它仍然持有一块挺大的控制设备的 市场. 如果速度不重要并且你想支持老式主板, 一个 ISA 实现要优于 PCI. 这个老标准 的另外一个好处是如 ...

  9. VIM 用正则表达式,非贪婪匹配,匹配竖杠,竖线, 匹配中文,中文正则,倒数第二列, 匹配任意一个字符 :

    VIM 用正则表达式 批量替换文本,多行删除,复制,移动 在VIM中 用正则表达式 批量替换文本,多行删除,复制,移动 :n1,n2 m n3     移动n1-n2行(包括n1,n2)到n3行之下: ...

  10. Team Foundation Server 2015使用教程【8】:读取器tfs组的checkin权限修改