手把手带你阅读Mybatis源码(一)构造篇(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 手把手带你阅读Mybatis源码(一)构造篇(上)

前言


今天会给大家分享我们常用的持久层框架——MyBatis的工作原理和源码解析,后续会围绕Mybatis框架做一些比较深入的讲解,之后这部分内容会归置到公众号菜单栏:连载中…-框架分析中,欢迎探讨!


说实话MyBatis是我第一个接触的持久层框架,在这之前我也没有用过Hibernate,从Java原生的Jdbc操作数据库之后就直接过渡到了这个框架上,当时给我的第一感觉是,有一个框架太方便了。


举一个例子吧,我们在Jdbc操作的时候,对于对象的封装,我们是需要通过ResultSet.getXXX(index)来获取值,然后在通过对象的setXXX()方法进行手动注入,这种重复且无任何技术含量的工作一直以来都是被我们程序猿所鄙视的一环,而MyBatis就可以直接将我们的SQL查询出来的数据与对象直接进行映射然后直接返回一个封装完成的对象,这节省了程序猿大部分的时间,当然其实JdbcTemplate也可以做到,但是这里先不说。


MyBatis的优点有非常多,当然这也只有同时使用过Jdbc和MyBatis之后,产生对比,才会有这种巨大的落差感,但这并不是今天要讨论的重点,今天的重心还是放在MyBatis是如何做到这些的。


对于MyBatis,给我个人的感受,其工作流程实际上分为两部分:第一,构建,也就是解析我们写的xml配置,将其变成它所需要的对象。第二,就是执行,在构建完成的基础上,去执行我们的SQL,完成与Jdbc的交互。而这篇的重点会先放在构建上。


Xml配置文件


玩过这个框架的同学都知道,我们在单独使用它的时候,会需要两个配置文件,分别是mybatis-config.xml和mapper.xml,在官网上可以直接看到,当然这里为了方便,我就直接将我的xml配置复制一份。


<!-- mybatis-config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 和spring整合后 environments配置将废除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理 -->
            <transactionManager type="JDBC" />
            <!-- 数据库连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                          value="jdbc:mysql://xxxxxxx:3306/test?characterEncoding=utf8"/>
                <property name="username" value="username" />
                <property name="password" value="password" />
            </dataSource>
        </environment>
    </environments>
    <!-- 加载mapper.xml -->
     <mappers>
         <!-- <package name=""> -->
         <mapper resource="mapper/DemoMapper.xml"  ></mapper>
     </mappers>
</configuration>


<!-- DemoMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper  namespace="com.DemoMapper">
    <select  id="queryTest"   parameterType="Map" resultType="Map">
        select * from test WHERE id =#{id}
    </select>
</mapper>


我们不难看出,在mybatis-config.xml这个文件主要是用于配置数据源、配置别名、加载mapper.xml,并且我们可以看到这个文件的<mappers>节点中包含了一个<mapper>,而这个mapper所指向的路径就是另外一个xml文件:DemoMapper.xml,而这个文件中写了我们查询数据库所用的SQL。


而,MyBatis实际上就是将这两个xml文件,解析成配置对象,在执行中去使用它。


解析


MyBatis需要什么配置对象?


虽然在这里我们并没有进行源码的阅读,但是作为一个程序猿,我们可以凭借日常的开发经验做出一个假设。假设来源于问题,那么问题就是:为什么要将配置和SQL语句分为两个配置文件而不是直接写在一起?


是不是就意味着,这两个配置文件会被MyBatis分开解析成两个不同的Java对象?


不妨先将问题搁置,进行源码的阅读。


环境搭建



首先我们可以写一个最基本的使用MyBatis的代码,我这里已经写好了。


public static void main(String[] args) throws Exception {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //创建SqlSessionFacory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    /******************************分割线******************************/
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获取Mapper
    DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("id","123");
    System.out.println(mapper.selectAll(map));
    sqlSession.close();
    sqlSession.commit();
  }


看源码重要的一点就是要找到源码的入口,而我们可以从这几行程序出发,来看看构建究竟是在哪开始的。


首先不难看出,这段程序显示通过字节流读取了mybatis-config.xml文件,然后通过SqlSessionFactoryBuilder.build()方法,创建了一个SqlSessionFactory(这里用到了工厂模式和构建者模式),前面说过,MyBatis就是通过我们写的xml配置文件,来构建配置对象的,那么配置文件所在的地方,就一定是构建开始的地方,也就是build方法。


构建开始


进入build方法,我们可以看到这里的确有解析的意思,这个方法返回了一个SqlSessionFactory,而这个对象也是使用构造者模式创建的,不妨继续往下走。


public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //解析mybatis-config.xml
      //XMLConfigBuilder  构造者
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //parse(): 解析mybatis-config.xml里面的节点
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }


进入parse():


public Configuration parse() {
    //查看该文件是否已经解析过
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //如果没有解析过,则继续往下解析,并且将标识符置为true
    parsed = true;
    //解析<configuration>节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }


注意parse的返回值,Configuration,这个似曾相识的单词好像在哪见过,是否与mybatis-config.xml中的<configuration>节点有所关联呢?


答案是肯定的,我们可以接着往下看。


看到这里,虽然代码量还不是特别多,但是至少现在我们可以在大脑中得到一个大致的主线图,也如下图所示:


image.png


沿着这条主线,我们进入parseConfiguration(XNode)方法,接着往下看。


private void parseConfiguration(XNode root) {
    try {
      //解析<Configuration>下的节点
      //issue #117 read properties first
      //<properties>
      propertiesElement(root.evalNode("properties"));
      //<settings>
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      //别名<typeAliases>解析
      // 所谓别名 其实就是把你指定的别名对应的class存储在一个Map当中
      typeAliasesElement(root.evalNode("typeAliases"));
      //插件 <plugins>
      pluginElement(root.evalNode("plugins"));
      //自定义实例化对象的行为<objectFactory>
      objectFactoryElement(root.evalNode("objectFactory"));
      //MateObject   方便反射操作实体类的对象
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //<environments>
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // typeHandlers
      typeHandlerElement(root.evalNode("typeHandlers"));
      //主要 <mappers> 指向我们存放SQL的xxxxMapper.xml文件
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }


可以看到这个方法已经在解析<configuration>下的节点了,例如<settings>,<typeAliases>,<environments><mappers>


这里主要使用了分步构建,每个解析不同标签的方法内部都对Configuration对象进行了set或者其它类似的操作,经过这些操作之后,一个Configuration对象就构建完毕了,这里由于代码量比较大,而且大多数构建都是些细节,大概知道怎么用就可以了,就不在文章中说明了,我会挑一个主要的说,当然有兴趣的同学可以自己去pull MyBatis的源码看看。


相关文章
|
2月前
|
SQL XML Java
mybatis-源码深入分析(一)
mybatis-源码深入分析(一)
|
1月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
51 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
1月前
|
Java 数据库连接 mybatis
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
136 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
|
3月前
|
XML Java 数据库连接
mybatis源码研究、搭建mybatis源码运行的环境
这篇文章详细介绍了如何搭建MyBatis源码运行的环境,包括创建Maven项目、导入源码、添加代码、Debug运行研究源码,并提供了解决常见问题的方法和链接到搭建好的环境。
mybatis源码研究、搭建mybatis源码运行的环境
|
3月前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。
|
3月前
|
供应链 前端开发 Java
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
该博客文章介绍了一个使用Mybatis、Layui、MVC和JSP技术栈开发的服装库存管理系统,包括注册登录、权限管理、用户和货号管理、库存管理等功能,并提供了源码下载链接。
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
|
3月前
|
缓存 Java 数据库连接
我要手撕mybatis源码
该文章深入分析了MyBatis框架的初始化和数据读写阶段的源码,详细阐述了MyBatis如何通过配置文件解析、建立数据库连接、映射接口绑定、动态代理、查询缓存和结果集处理等步骤实现ORM功能,以及与传统JDBC编程相比的优势。
我要手撕mybatis源码
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
118 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
56 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
351 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
下一篇
无影云桌面