Mybatis源码分析

702 阅读13分钟

参考资料:MyBatis原理深入解析

整体架构

架构设计

Mybatis按照功能可以划分为三层:

  • **API接口层:**对外提供操作数据库的api。mybatis和数据库的交互有两种方式,使用sqlSession的api,或者使用Mapper代理的方式;
  • **数据处理层:**负责参数设置、SQL解析和执行,以及结果映射
  • **基础支撑层:**提供最基础的功能支撑,包括数据库连接、事务管理、缓存配置等等
image-20201117091913807

层次结构

mybatis通过sqlSession完成一次sql操作,需要经历以下几个步骤。

image-20201117214048468

SqlSession方式访问

mybatis提供了两种方式让我们去访问数据库,一种是通过调用sqlSession的api,另一种是通过获取mapper接口的代理对象,直接调用其方法。接下来,我们先分析sqlSession如何完成与数据库的交互。

 public void test1() throws IOException {
    // 1. 读取配置文件,转成字节输入流
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 2. 解析配置文件,封装Configuration对象   创建DefaultSqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 3. 创建DefaultSqlsession实例对象  设置事务不自动提交  完成executor对象的创建
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 4.(1)根据statementid,从Configuration中map集合中获取到了指定的MappedStatement对象
   //    (2)将查询任务委派了executor执行器
    List<Object> objects = sqlSession.selectList("namespace.id");

    // 5.释放资源
    sqlSession.close();
 }

总结

由于调用过程比较复杂,因此,先简单说明下整体思路,这样不容易乱。

主线脉络:SqlSessionFactory -> SqlSession -> Executor -> StatementHandler -> ParameterHandler -> ResultSetHandler

**1.SqlSessionFactory:**通过XMLConfigBuilder解析配置文件各种标签,把配置信息保存在Configuration对象,调用有参构造方法,传递Configuration对象,创建DefaultSqlSessionFactory

**2.SqlSession#创建对象:**调用SqlSessionFactory#openSession方法,先新建一个SimpleExecutor对象,执行有参构造方法,传入executor和configuration,实例化DefaultSqlSession对象

**3.SqlSession#查询:**根据statement id,即namespace+id,从configuration中获取对应的mappedStatement,交给executor,由执行器完成查询操作

4.Executor#查询:

  • 根据传参,把#{}替换成?占位符,解析生成动态sql
  • 建立查询缓存
  • 创建StatementHandler对象,把查询操作委派给它

5.StatementHandler#查询:

  • 创建Statement对象

  • ParameterHandler完成参数设置,替换?占位符为参数值

  • Statement调用execute方法,执行查询操作

  • ResultSetHandler处理结果集,如果是selectList操作,则返回List集合

会话工厂

第1~2步是mybatis的创建会话工厂过程,读取配置文件,把配信息保存到Configuration对象中。第1步是读取配置文件,转成字节输入流,这个过程我们就不看了,直接去探究怎么创建会话工厂

SqlSessionFactory

SqlSessionFactoryBuilder#build(java.io.InputStream)

public SqlSessionFactory build(InputStream inputStream) {
    //调用了重载方法
    return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // 创建 XMLConfigBuilder(专门解析mybatis配置文件的类)
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        
        // 执行 XML 解析。parser.parse()返回值为Configuration对象 
        // 再调用重载方法,build
        return build(parser.parse());
    }
    ... 省略部分代码
}

在build方法中,创建XMLConfigBuilder对象,用于解析转成字节流的配置信息,保存到Configuration对象。接着,再次调用build重载方法。

Configuration

由于Configuration比较重要,先简单介绍下。

Configuration对象用于保存配置文件信息,它的结构和xml配置文件几乎相同,配置文件的每个标签都有对应的内部属性。

xml标签:settings、typeAliases、objectFactory、mapper等

Configuration部分属性

   /**
     * 变量 Properties 对象。
     *
     * 参见 {@link org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement(XNode context)} 方法
     */
    protected Properties variables = new Properties();
    /**
     * ReflectorFactory 对象
     */
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    /**
     * ObjectFactory 对象
     */
    protected ObjectFactory objectFactory = new DefaultObjectFactory();

XMLConfigBuilder#parse

XMLConfigBuilder#parse

	/**
     * 解析 XML 成 Configuration 对象。
     *
     * @return Configuration 对象
     */
    public Configuration parse() {
       	... 省略部分代码
        
        // 解析 XML configuration 节点
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }

调用parseConfiguration方法解析mybatis配置文件中的各种标签,并把配置信息把保存到Configuration对象

	/**
     * 解析 XML
     *
     * 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根节点
     */
    private void parseConfiguration(XNode root) {
        try {
            ... 各种标签,省略
            // 赋值 <settings /> 到 Configuration 属性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析 <environments /> 标签
            environmentsElement(root.evalNode("environments"));
            // 解析 <mappers /> 标签
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

MappedStatement

在解析配置文件时,<insert><update>等标签会被封装成一个MappedStatement对象,然后存储在Configuration对象的mappedStatements属性中,mappedStatements是一个HashMap,key是statement id,即namespace + id,value是MappedStatement对象。

<configuration>
   <!--引入映射配置文件-->
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
    </mappers>
</configuration>


<!--namespace : 名称空间:与id组成sql的唯一标识-->
<mapper namespace="com.lagou.dao.IUserDao">
    <!--添加用户-->
    <!--parameterType:参数类型-->
    <insert id="saveUser" parameterType="user" >
        insert into user values(#{id},#{username})
    </insert> 
</mapper>
 	/**
     * MappedStatement 映射
     *
     * KEY:`${namespace}.${id}`
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");

DefaultSqlSessionFactory

至此,xml文件解析完成,调用build方法,创建默认的会话工厂。SqlSessionFactoryBuilder#build

/**
     * 创建 DefaultSqlSessionFactory 对象
     *
     * @param config Configuration 对象
     * @return DefaultSqlSessionFactory 对象
     */
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config); 
    }

总结

创建SqlSessionFactory时,先读取配置文件,转成字节输入流。接着,通过XMLConfigBuilder#parseConfiguration方法解析xml标签,把配置信息保存到Configuration对象的内部属性中,对于mapper文件中sql,会生成对应的MappedStatement,存储到HashMap中,key是mapper文件命名空间+方法名。最后,调用有参构造方法,传入Configuration对象,创建DefaultSqlSessionFactory

SqlSession

SqlSession是Mybatis用来与数据库交互的顶层接口,通常将它与ThreadLocal绑定,一次会话使用同一个SqlSession,使用完还需要关闭。有两个实现类:DefaultSqlSession(默认)和SqlSessionManager(弃用)。

DefaultSqlSession有两个重要参数,configuration保存解析后配置信息,executor为执行器,各种sql操作都是委派给执行器完成。

public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;
    private final Executor executor;
    ...
}

创建对象

刚刚我们已经分析完第1~2步,接下来就是第3步,通过会话工厂的openSession()获取sqlSession

 public void test1() throws IOException {
	......
    // 3. 创建DefaultSqlsession实例对象   设置事务不自动提交  完成executor对象的创建
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 4.(1)根据statementid,从Configuration中map集合中获取到了指定的MappedStatement对象
   //    (2)将查询任务委派了executor执行器
    List<Object> objects = sqlSession.selectList("namespace.id");

    // 5.释放资源
    sqlSession.close();
 }

DefaultSqlSessionFactory#openSession()

@Override
public SqlSession openSession() {
    //getDefaultExecutorType()传递的是SimpleExecutor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 创建 Executor 对象
        final Executor executor = configuration.newExecutor(tx, execType);
        
        // 创建 DefaultSqlSession 对象
        return new DefaultSqlSession(configuration, executor, autoCommit);
    }
    ... 省略部分代码
}

调用api

拿到会话后,我们就可以调用相关的api操作数据库,继续执行第4步

// 4. (1)根据statementid,从Configuration中map集合中获取到了指定的MappedStatement对象
//    (2)将查询任务委派了executor执行器
List<Object> objects = sqlSession.selectList("namespace.id");

查看sqlSession的selectList实现方法,DefaultSqlSession#selectList

@Override
public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
}

@Override
public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 获得 MappedStatement 对象
        MappedStatement ms = configuration.getMappedStatement(statement);
        // 执行查询   RowBounds -> 用于逻辑分页   wrapCollection(parameter) -> 用来装饰数组或集合
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    }
    ... 省略部分代码
}

可以看到,selectList方法,根据statement id,从configuration的mappedStatements中获取对应的mappedStatement对象,把他交给执行器,由执行器完成查询操作。

总结

创建对象:会话工厂调用openSession方法,内部会执行DefaultSqlSession的有参构造方法,设置configuration、executor属性。

调用api:通过namespace+idcongfiguration中获取对应的mappedStatement,把它交给executor,由执行器完成相关数据库操作

Executor

在前面创建SqlSession时,我们传递的执行器是SimpleExecutor,接下来找到对应的query方法。但是,由于SimpleExecutor继承于BaseExecutor,本身自己并没有重写query方法,所以执行的是BaseExecutor#query方法

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                                                               ResultHandler resultHandler) throws SQLException {
    // 根据传入的参数,动态解析SQL语句,把#{}替换成?,返回BoundSql,提供给后续的PrepareStatement使用
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 为本次查询创建缓存的Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
   
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                         ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    
   	... 省略部分代码
    try {
        // 从一级缓存中,获取查询结果
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
       
        if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

        } else {
            // 获取不到,从数据库中读取
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } 
    ... 省略部分代码
    return list;
}
// 从数据库中读取操作
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                                      ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
   
    // 执行读操作
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    ... 省略部分代码
    return list;
}

继续往下执行,调用SimpleExecutor#doQuery方法

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    ... 省略部分代码
    
    Configuration configuration = ms.getConfiguration();
    
    // 创建StatementHanlder对象
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
                                                                 rowBounds, resultHandler, boundSql);
    // 创建jdbc中的statement对象
    stmt = prepareStatement(handler, ms.getStatementLog());
    
    // StatementHandler执行读操作
    return handler.query(stmt, resultHandler);
}

经过多个方法嵌套调用,Executor#query方法最终会创建一个StatementHandler对象,把查询操作委派给StatementHandler完成。

总结

Executor执行过程:

1.根据传参,完成sql语句的动态解析,将#{}替换成?,生成BoundSql对象,供StatementHandler对象使用

2.为提高性能,创建查询缓存

3.创建StatementHandler,由StatementHandler完成数据库的查询操作

StatementHandler

StatementHandler主要完成两个工作:

  • 参数设置:通过调用ParameterHandler来完成sql参数设置,替换占位符
  • 结果集封装:通过使用ResultSetHandler封装Statement返回的结果集

参数设置

继续从doQuery方法跟踪代码

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
     ... 省略部分代码
         
    // 创建jdbc中的statement对象
    stmt = prepareStatement(handler, ms.getStatementLog());
    // StatementHandler执行读操作
    return handler.query(stmt, resultHandler);
}

// 创建PrepareStatement对象,初始化StatementHandler对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获得 Connection 对象
    Connection connection = getConnection(statementLog);
    // 创建 Statement 或 PrepareStatement 对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符
    handler.parameterize(stmt);
    return stmt;
}


@Override
public void parameterize(Statement statement) throws SQLException {
    //使用ParameterHandler对象来完成对Statement的设值
    parameterHandler.setParameters((PreparedStatement) statement);
}

parameterize方法调用ParameterHandler#setParameters,根据传入参数,把statement对象的?占位符替换成参数值

结果集封装

进入handler#query方法,查看里面执行过程。

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    ... 省略部分代码
        
    //  StatementHandler执行读操作
    return handler.query(stmt, resultHandler);
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    // 执行查询
    statement.execute(sql);
    // 处理返回结果
    return resultSetHandler.handleResultSets(statement);
}
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 获得 ResultMap 数组
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount); // 校验
    while (rsw != null && resultMapCount > resultSetCount) {
        // 获得 ResultMap 对象
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 处理 ResultSet ,将结果添加到 multipleResults 中
        handleResultSet(rsw, resultMap, multipleResults, null);
        // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
        rsw = getNextResultSet(stmt);
        // 清理
        cleanUpAfterHandlingResultSet();
        // resultSetCount ++
        resultSetCount++;
    }
    ... 省略部分代码

    // 如果是 multipleResults 单元素,则取首元素返回
    return collapseSingleResultList(multipleResults);
}

执行器调用query方法,实际上是JDBC的Statement对象执行sql,然后使用ResultSetHandler把结果集转成List集合。

总结

StatementHandler主要完成两件事情:

1.由ParameterHandler完成参数设置,把sql中?占位符替换成参数值

2.statement执行sql语句,由ResultSetHandler处理结果集,如果是查询操作,转成List集合

Mapper方式访问

除了直接调用SqlSession的api操作数据库外,mybatis还提供通过Mapper接口的代理对象,完成与数据库的交互。

	/**
     * mapper代理方式
     */
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = factory.openSession();

        // 使用JDK动态代理对mapper接口产生代理对象
        IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);

        //代理对象调用接口中的任意方法,执行的都是动态代理中的invoke方法
        List<Object> allUser = mapper.findAllUser();
    }

MapperRegistry

在开始分析Mapper方式前,先介绍MapperRegistry,它是Configuration的一个属性,内部维护一个HashMap,key是mapper接口的class对象,value是每个接口对应的代理工厂类。

 /**
  * MapperRegistry 对象
  */
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public class MapperRegistry {
    /**
     * MyBatis Configuration 对象
     */
    private final Configuration config;
    /**
     * MapperProxyFactory 的映射
     *
     * KEY:Mapper 接口
     */
    // 这个类中维护一个HashMap存放MapperProxyFactory
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
}

在解析mappers标签时,会判断配置的是xml文件,还是Mapper接口或者包名。

<mappers>
    <mapper resource="UserMapper.xml"></mapper>

    <package name="com.drh.mapper"/>
    <mapper class="com.drh.mapper.UserMapper"></mapper>
</mappers>
  • 如果是xml文件,会解析<insert><select>等标签,封装成MappedStatement对象,存储到Configuration的mappedStatement

  • 如果是接口或包名,会给mapper接口创建一个对应的MapperProxyFactory对象,存储到Map,key是接口的class对象,value是代理工厂类

    • org.apache.ibatis.binding.MapperRegistry#addMapper

    • public <T> void addMapper(Class<T> type) {
          // 判断,必须是接口。
          if (type.isInterface()) {
              // 已经添加过,则抛出 BindingException 异常
              if (hasMapper(type)) {
                  throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
              }
              boolean loadCompleted = false;
              try {
                  // 添加到 knownMappers 中
                  knownMappers.put(type, new MapperProxyFactory<>(type));
                  
                  ... 省略部分代码
          }
      }
      

getMapper

开始分析,进入DefaultSqlSession#getMapper方法

@Override
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}

Configuration#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    
    // 获得 MapperProxyFactory 对象
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    
    // 不存在,则抛出 BindingException 异常
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
  
    try {
        // 返回代理对象
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

org.apache.ibatis.binding.MapperProxyFactory#newInstance

public T newInstance(SqlSession sqlSession) {
    
    // 创建 实现了JDK动态代理的invocationHandler接口的mapperProxy对象
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    
    // 调用了重载方法
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
    // 调用JDK动态代理创建Mapper代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

调用Mapper方法

既然我们已经知道在getMapper时,返回的是mapper接口的代理对象,因此,调用mapper接口的方法时,会被代理对象拦截到。根据前面的分析,这个拦截方法就是MapperProxy#invoke方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ... 省略部分代码
        
    // 获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 重点在这:MapperMethod最终调用了执行的方法
    return mapperMethod.execute(sqlSession, args);
}

MapperMethod#execute

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    
    //判断mapper中的方法类型,最终调用的还是SqlSession中的方法
    switch (command.getType()) {
        case INSERT: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 执行 INSERT 操作
            // 转换 rowCount
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATE: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
        }
        case DELETE: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
        }
        case SELECT:
            // 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
                
                // 执行查询,返回列表
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
                
                // 执行查询,返回 Map
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
                
                // 执行查询,返回 Cursor
            } else if (method.returnsCursor()) {
                result = executeForCursor(sqlSession, args);
                
                // 执行查询,返回单个对象
            } else {
                // 转换参数
                Object param = method.convertArgsToSqlCommandParam(args);
                // 查询单条
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional() &&
                    (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + command.getName());
    }
    ... 省略部分代码
    // 返回结果
    return result;
}

MapperProxy#invoke方法中,创建MapperMethod对象,并调用其execute方法。通过匹配mapper方法的类型(增/删/改/查),最终还是调用sqlSession的api完成数据库操作。

总结

Mapper方式访问数据库,主要分为两步:获取代理对象,和执行接口方法

  • 获取代理对象:
    • 解析mapper标签,如果指定的是接口或者包名,MapperRegistry会给每个接口创建一个代理工厂类,同时使用一个HashMap保存这个对应关系,key是接口的class对象
    • 调用getMapper,经过多层方法调用(sqlSession -> configuration -> mapperRegistry),获取到Mapper接口对应的MapperProxyFactory,然后调用newInstance方法。创建实现了InvocationHandler接口的MapperProxy对象,最后通过JDK动态代理,创建Mapper接口的代理对象
  • 执行接口方法:调用mapper方法时,实际是执行MapperProxy#invoke方法。创建MapperMethod对象,调用其execute方法,匹配mapper方法的类型,最终选择SqlSession对应的api完成数据库操作。