mybatis 架构原理 和 源码刨析

一、mybatis 架构原理

1、架构设计

mybatis 的功能架构分为三层:

1)API 接口层:

提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
myBatis 和数据库的交互有两种方式:

  1. 使用传统的 myBatis 提供的 API;
  2. 使用 mapper 代理的方式;

2)数据处理层:

负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

3)基础支撑层:

负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

2、主要构件及其相互关系

构件描述
SqlSession作为 mybatis 工作的主要顶层 API,表示和数据库交互的会话,完成必要数据库增删改查功能。
Executormybatis 执行器,是 mybatis 调度的核心,负责 SQL 语句的生成和查询缓存的维护。
StatementHandler封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将 Statement 结果集转换成 List 集合。
ParameterHandler负责对用户传递的参数转换成 JDBC Statement 所需要的参数。
ResultSetHandler负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。
TypeHandler负责 java 数据类型和 jdbc 数据类型之间的映射和转换。
MappedStatementMappedStatement 维护了一条 < select
SqlSource负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回。
BoundSql表示动态生成的 SQL 语句以及相应的参数信息。

在这里插入图片描述

3、总体流程

1)加载配置并初始化

触发条件:加载配置文件;
配置来源于两个地方,一个是配置文件(主配置文件conf.xml,mapper文件*.xml),—个是 java 代码中的注解,将主配置文件内容解析封装到 Configuration,将 sql 的配置信息加载成为一个 mappedstatement 对象,存储在内存之中。

2)接收调用请求

触发条件:调用 Mybatis 提供的 API;
传入参数:为 SQL 的 ID 和传入参数对象;
处理过程:将请求传递给下层的请求处理层进行处理。

3)处理操作请求

触发条件:API 接口层传递请求过来;
传入参数:为SQL的ID和传入参数对象;
处理过程

  1. 根据 SQL 的 ID 查找对应的 MappedStatement 对象。
  2. 根据传入参数对象解析 MappedStatement 对象,得到最终要执行的 SQL 和执行传入参数。
  3. 获取数据库连接,根据得到的最终 SQL 语句和执行传入参数到数据库执行,并得到执行结果。
  4. 根据 MappedStatement 对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
  5. 释放连接资源。

4)返回处理结果

将最终的处理结果返回。

二、Mybatis 源码刨析

1、配置初始化分析

mybatis 版本 3.5.5

1)初始化测试代码片段

Inputstream inputstream = Resources.getResourceAsStream("mybatis-config.xml");
// 这一行代码正是初始化工作的开始。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

2)进入源码分析,从 SqlSessionFactoryBuilder 的 build 方法开始

// 1.我们最初调用的build
public SqlSessionFactory build(InputStream inputStream) {
    // 调用了重载方法
    return build(inputStream, null, null);
}

// 2.调用的重载方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // XMLConfigBuilder 是专门解析 mybatis 的配置文件的类
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // 这里又调用了一个重载方法。parser.parse() 的返回值是 Configuration 对象
        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.
        }
    }
}

3)配置文件解析 Configuration

mybatis 在初始化的时候,会将 mybatis 的配置信息全部加载到内存中,使用 org.apache.ibatis.session.Configuration 实例来维护。
Configuration 对象介绍:

Configuration对象的结构和xml配置文件的对象几乎相同。
回顾一下xml中的配置标签有哪些:    
properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理器),objectFactory (对象工厂),mappers (映射器)等。
Configuration也有对应的对象属性来封装它们。
也就是说,初始化配置文件信息的本质就是创建 Configuration 对象,将解析的xml数据封装到 Configuration 内部属性中。

上面代码中 parser.parse() 调用的具体代码,是 XMLConfigBuilder 类中 parse() 方法:

/**
 * 解析 XML 成 Configuration 对象。
 */
public Configuration parse() {
    // 若已解析,抛出 BuilderException 异常
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    // 标记已解析
    parsed = true;
    // 解析 XML configuration 节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

/** 
 * 解析XML
 */
private void parseConfiguration(XNode root) {
    try {
        // issue #117 read properties first
        // 解析 <properties /> 标签
        propertiesElement(root.evalNode("properties"));
        // 解析 <settings /> 标签
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        // 加载自定义的 VFS 实现类
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        // 解析 <typeAliases /> 标签
        typeAliasesElement(root.evalNode("typeAliases"));
        // 解析 <plugins /> 标签
        pluginElement(root.evalNode("plugins"));
        // 解析 <objectFactory /> 标签
        objectFactoryElement(root.evalNode("objectFactory"));
        // 解析 <objectWrapperFactory /> 标签
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        // 解析 <reflectorFactory /> 标签
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        // 赋值 <settings /> 至 Configuration 属性
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        // 解析 <environments /> 标签
        environmentsElement(root.evalNode("environments"));
        // 解析 <databaseIdProvider /> 标签
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        // 解析 <typeHandlers /> 标签
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析 <mappers /> 标签
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

4)介绍 MappedStatement

作用:MappedStatement 与 Mapper 配置文件中的一个 select/update/insert/delete 节点相对应。
mapper 中配置的标签都被封装到了此对象中,主要用途是描述一条 SQL 语句。
初始化过程:回顾刚开始介绍的加载配置文件的过程中,会对 mybatis-config.xml 中的各个标签都进行解析,
其中有 mappers 标签用来引入 mapper.xm l文件或者配置 mapper 接口的目录。

<select id="getUser" resultType="user">
    select * from user where id=#{id}
</select>

这样的一个 select 标签会在初始化配置文件时被解析封装成一个 MappedStatement 对象,然后存储在 Configuration 对象的 mappedStatements 属性中,mappedStatements 是一个 HashMap,存储时 key = 全限定类名 + 方法名,value = 对应的 MappedStatement 对象。

在 configuration 中对应的属性为:

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
        .conflictMessageProducer((savedValue, targetValue) -> 
        ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

在 XMLConfigBuilder 中的处理调用方法是 mapperElement(root.evalNode(“mappers”));

5)结束,返回

到此对 xml 配置文件的解析就结束了,回到步骤2.中 SqlSessionFactoryBuilder 调用的重载 build 方法中。

// 5.调用的重载方法
public SqlSessionFactory build(Configuration config) {
    // 创建了 DefaultSqlSessionFactory 对象,传入 Configuration 对象。
    return new DefaultSqlSessionFactory(config);
}

2、调用 SqlSession,执行 SQL 语句流程刨析

1)SqlSession 介绍

SqlSession 是一个接口,它有两个实现类:DefaultSqlSession(默认)和 SqlSessionManager(弃用,不做介绍)。
SqlSession 是 MyBatis 中用于和数据库交互的顶层类,通常将它与 ThreadLocal 绑定,一个会话使用一个 SqlSession,并且在使用完毕后需要 close。

public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration;
    private final Executor executor;
}

SqlSession 中的两个最重要的参数,configuration 与初始化时的相同,Executor 为执行器。

源码分析,从测试代码开始,初始化完毕后,我们就要执行 SQL 了。

SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");

DefaultSqlSessionFactory 类中获得 sqlSession:

// 6. 进入 openSession 方法。
@Override
public SqlSession openSession() {
    // getDefaultExecutorType() 传递的是 SimpleExecutor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

// 7. 进入 openSessionFromDataSource。
// ExecutorType 为 Executor 的类型,TransactionIsolationLevel 为事务隔离级别,autoCommit 是否开启事务。
// openSession 的多个重载方法可以指定获得的 SqlSession 的 Executor 类型和事务的处理。
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);
    } catch (Exception e) {
        // may have fetched a connection so lets call close()
        closeTransaction(tx); 
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

执行 sqlsession 中的 api:

// 8.进入 selectList 方法,多个重载方法。
@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 {
        // 根据传入的全限定名 + 方法名从映射的 Map 中取出 MappedStatement 对象
        MappedStatement ms = configuration.getMappedStatement(statement);
        // 调用 Executor 中的方法处理
        // RowBounds 是用来逻辑分页
        // wrapCollection(parameter) 是用来装饰集合或者数组参数
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

2)Executor

Executor 也是一个接口,他有三个常用的实现类:
BatchExecutor (重用语句并执行批量更新)
ReuseExecutor (重用预处理语句 prepared statements)
SimpleExecutor (普通的执行器,默认)

继续源码分析步骤,进入 executor.query():

// 此方法在 SimpleExecutor 的父类 BaseExecutor 中实现
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 根据传入的参数动态获得 SQL 语句,最后返回用 BoundSql 对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 为本次查询创建缓存的 Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

// 进入 query 的重载方法中
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, 
        BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        queryStack++;
        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);
        }
    } finally {
        queryStack--;
    }
    if (queryStack == 0) {
        for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
        }
        // issue #601
        deferredLoads.clear();
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
        }
    }
    return list;
}

// 从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, 
        CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        // 查询的方法
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        localCache.removeObject(key);
    }
    // 将查询结果放入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

// SimpleExecutor 中实现父类的 doQuery 抽象方法
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, 
        BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        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);
    } finally {
        closeStatement(stmt);
    }
}

// 创建 Statement 的方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  // 条代码中的 getConnection 方法经过重重调用最后会调用 openConnection 方法,从连接池中获得连接。
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

// 从连接池获得连接的方法
protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

上述的 Executor.query() 方法几经转折,最终会有以下过程:

  1. 创建一个 StatementHandler 对象;
  2. 将必要的参数传递给 StatementHandler;
  3. 使用 StatementHandler 来完成对数据库的查询;
  4. 返回 List 结果集。

从上面的代码中我们可以看出,Executor 的功能和作用是:

  1. 根据传递的参数,完成 SQL 语句的动态解析,生成 BoundSql 对象,供 StatementHandler 使用;
  2. 为查询创建缓存,以提高性能;
  3. 创建 JDBC 的 Statement 连接对象,传递给 StatementHandler 对象,返回 List 查询结果。

3)StatementHandler

StatementHandler 对象主要完成两个工作:

  • 对于 JDBC 的 PreparedStatement 类型的对象,创建的过程中,我们使用的是 SQL 语句字符串会包含若干个 ? 占位符,我们其后再对占位符进行设值。
  • StatementHandler 通过 parameterize(statement) 方法对 Statement 进行设值; StatementHandler 通过 List query(Statement statement, ResultHandler resultHandler) 方法来完成执行 Statement,和将 Statement 对象返回的 resultSet 封装成 List;

进入源码分析,StatementHandler 实现类 PreparedStatementHandler 中的 parameterize(statement) 方法:

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

DefaultParameterHandler.类中的 setParameters 方法:

/**
 * ParameterHandler 类的 setParameters(PreparedStatement ps) 实现对某一个 Statement 进行设置参数。
 */
@Override
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                // issue #448 ask first for additional params
                if (boundSql.hasAdditionalParameter(propertyName)) { 
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                // 每一个 Mapping 都有一个 TypeHandler,根据 TypeHandler 来对 preparedStatement 进行设置参数
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    // 设置参数
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

从上述的代码可以看到:

  1. StatementHandler 的 parameterize(Statement) 方法调用了 ParameterHandler 的 setParameters(statement) 方法;
  2. ParameterHandler 的 setParameters(Statement) 方法负责根据我们输入的参数,对 statement 对象的 ? 占位符处进行赋值。

继续源码分析,进入到 StatementHandler 的 List query(Statement statement, ResultHandler resultHandler)方法(在executor 中 doQuery 方法中调用):

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // 1.调用 preparedStatemnt。execute() 方法,然后将 resultSet 交给 ResultSetHandler 处理
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 2.使用 ResultHandler 来处理 ResultSet
    return resultSetHandler.handleResultSets(ps);
}

从上述代码我们可以看出,
StatementHandler 的 List query(Statement statement, ResultHandler resultHandler) 方法的实现,是调用了 ResultSetHandler 的 handleResultSets(Statement) 方法。

4)ResultSetHandler

ResultSetHandler 的 handleResultSets(Statement) 方法会将 Statement 语句执行后生成的 resultSet 结果集转换成 List 结果集。

@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++;
    }

    // 因为 'mappedStatement.resultSets' 只在存储过程中使用,本系列暂时不考虑,忽略即可
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
        while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    }

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

3、Mapper 代理方式:

1)回顾一下 mapper 使用方式

public static void main(String[] args) {
    
    //前三步都相同
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = factory.openSession(); 
    // 这里不再调用SqlSession的api,而是获得了接口对象,调用接口中的方法。
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.getUserByName("tom");
}

通常 Mapper 接口没有实现的方法却可以使用的原因:
开始之前介绍一下 MyBatis 初始化时对接口的处理:MapperRegistry 是 Configuration 中的一个属性,
它内部维护一个 HashMap 用于存放 mapper 接口的工厂类,每个接口对应一个工厂类。
mappers 中可以配置接口的包路径,或者某个具体的接口类。

<mappers>
    <mapper class="com.lagou.mapper.UserMapper" />
    <package name="com.lagou.mapper" />
</mappers>

当解析 mappers 标签时,它会判断解析到的是 mapper 配置文件时,会再将对应配置文件中的增删改查签封装成 MappedStatement 对象,存入 mappedStatements 中。
当判断解析到接口时,会建此接口对应的 MapperProxyFactory 对象,存入 HashMap 中,key = 接口的字节码对象,value = 此接口对应的 MapperProxyFactory 对象。

2)源码解析 getMapper()

进入 sqlSession.getMapper(UserMapper.class) 分析源代码:

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:

@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从 MapperRegistry 中的 HashMap 中拿 MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    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);
    }
}

MapperProxyFactory 中的 newInstance:

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

MapperProxy 类,实现了 InvocationHandler 接口:

public class MapperProxy<T> implements InvocationHandler, Serializable {

    // 省略部分源码
    
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache;

    // 构造,传入了 SqlSession,说明每个 session 中的代理对象的不同的!
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    
    // 省略部分代码
}

3)源码剖析 invoke(),即 mapper 接口对象的调用

在动态代理返回了示例后,我们就可以直接调用 mapper 类中的方法了,但代理对象调用方法,执行是在 MapperProxy 中的 invoke 方法中。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 如果是 Object 定义的方法,直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            // cachedInvoker 方法获得 MapperMethodInvoker 对象
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
}

// PlainMethodInvoker 是 MapperProxy 的内部类, 并且实现了 MapperMethodInvoker 接口。
private static class PlainMethodInvoker implements MapperMethodInvoker {
    // 获得 MapperMethod 对象
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
        super();
        this.mapperMethod = mapperMethod;
    }

    // 重点在这:MapperMethod 最终调用了执行的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
        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());
    }
    // 返回结果为 null,并且返回类型为基本类型,则抛出 BindingException 异常
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    // 返回结果
    return result;
}

文章内容输出来源:拉勾教育Java高薪训练营;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jason559

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值