MyBatis源码学习笔记(一)——核心组件

MyBatis

一. demo环境

使用Maven搭建的项目,MyBatis版本为3.4.6,使用从 XML 中构建 SqlSessionFactory的方式来使用MyBatis,具体可以参考https://github.com/BrightLoong/mylab/tree/master/mybatis-learn,单元测试代码如下:

package io.github.brightloong.mybatis.learn;

import io.github.brightloong.mybatis.learn.mapper.BindRecordMapper;
import io.github.brightloong.mybatis.learn.pojo.BindRecord;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MapperTest {
    private SqlSession sqlSession;

    @Before
    public void before() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.close();
    }

    @Test
    public void testSelect() {
        BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class);
        List<BindRecord> bindRecord = mapper.selectAll();
        Assert.assertNotNull(bindRecord);
    }
}

二. 概述

从上面的代码可以看出,MyBatis的运行主要围绕以下几个核心组件:

  • SqlSessionFactoryBuilder:根据配置信息(比如xml配置信息)或者代码来生成SqlSessionFactory。
  • SqlSessionFactory :工厂接口,SqlSessionFactoryBuilder默认生成的是DefaultSqlSessionFactory。
  • SqlSession:同样也是接口,MyBatis默认实现是DefaultSqlSession;SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句,也可以通过SqlSession获取Mapper接口,如上面的BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class);
  • 映射器实例(Mapper Instances):映射器是一个你创建来绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。一般来说由一个Java接口和XML文件(或者注解)构成,给出对应的SQL和映射实例。

三. 获取SqlSession

看看主要流程的时序图,了解每一步主要在做什么。

1. 获取SqlSession时序图

get sqlSession
  • 1.1:xml配置读入为inputStream
  • 1.2:实例化SqlSessionFactoryBuilder
  • 1.2:调用SqlSessionFactoryBuilder.build,解析xml配置,将xml的配置加载到Configuration供其他对象使用,然后返回SqlSessionFactory
  • 1.3:SqlSessionFactory.openSession返回DefaultSqlSession,下面同样通过时序图了解下openSession主要做了那些事

2. SqlSessionFactory.openSession()时序图

SqlSessionFactory.openSession
  • 1.1:调用DefaultSqlSessionFactory.openSession()最后实际调用的是openSessionFromDataSource
    • 1.1.1-1.12:构造事务相关的一些对象,用于构造Executor
    • 1.1.3:Configuration.newExecutor,返回Executor用于构造DefaultSqlSession,Executor是SqlSession的核心依赖对象,用于调度其他对象来处理参数、完成数据库操作和返回结果。
    • 1.1.3:返回DefaultSqlSession

三. 获取Mapper

通过BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class); 返回了Mapper实例,调用对应的方法就会执行相应的SQL。我们并没有对BindRecordMapper这个接口进行实现,那么方法又是怎么执行成功的呢。通过调试我们发现,实际返回的是代理类。关于代理的一些概念,请参考之前的文章Java静态代理&动态代理笔记

实际返回的代理类

通过跟踪源码,发现Mapper通过JDK的动态代理来实现。具体代码在MapperProxyFactory中,代码如下:

 protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

通过JDK动态代理,返回了MapperProxy。关键代码位于MapperProxy中,通过JDK动态代理相关知识可以知道,当调用被代理对象的方法的时候,实际执行的是代理对象中的invoke()方法。所以当调用mapper的对应方法的时候,实际调用的是MapperProxy中的invoke()方法。

四. 总结

通过上面的分析梳理下MyBatis的核心组件的关联。

核心组件关联

推荐阅读更多精彩内容