Mybatis原理剖析

架构设计

通过上面的架构图,我们可以看出,我们把Mybatis的功能分为三层:

  1. API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操作数据库。接口层一接到调用请求就会调用数据处理层来完成具体的数据处理。可以通过传统Mybatis提供的APIMapper代理方式
  2. 数据处理层;负责具体的SQL查找,SQL解析,SQL执行和执行结果映处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  3. 基础支持层:负责最基础的功能支撑,包括连接管理,事务管理,配置加载和缓存处理,这些都是共用的东西,将它们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑。

主要构建及其相互关系

构建

描述

SqlSession

作为Mybatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库CRUD功能

Executor

Mybatis执行器,是Mybatis调度的核心,负责SQL语句的生成和查询缓存的维护

StatmentHandler

封装了JDBC Statement操作,负责JDBC statement的操作,如设置参数,将Statement结果集转换为List集合

ParameterHandler

负责对用户传递的参数转换成JDBC Statement所需要的参数

ResultHandler

负责将JDBC返回的ResultSet结果集对象转换成List类型的集合

TypeHandler

负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatement

MappedStatement维护了一条<select | update | delete | insert> 节点的分装

SqlSource

负责根据用户传递的parameterObject,动态的生成SQL语句,将信息封装到BoundSql对象中,并返回

BoundSql

表示动态生成的SQL语句以及相应的参数信息

总统流程

加载配置文件并优化:

  1. 触发条件:加载配置文件,配置来源两个地方,一个是配置文件(主配置文件conf,xml,mapper文件*,xml),一个是java代码中的注解,将主配置文字文件内容解析封装到Configuration,将sql的配置信息加载成一个mappedstatement对象,存储在内存之中。
  2. 接受调用请求:触发条件(调用mybatis提供的API),传入参数(为SQL的ID和传入参数对象),处理过程(将请求传递给下层的请求处理层进行处理)。
  3. 处理操作请求:触发条件(API接口层请求过来),传入参数(为SQL的ID和传入参数对象),处理过程(A.根据sql的ID查找对应的MappendStatment对象;B.根据传入参数对象解析MappedStatement,得到最终要执行的SQL和执行传入参数;C.获取数据库连接,根据的得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果;D.根据MappedStatement对象中的结果映射配置对得到的执行结果解转换处理,并得到最终的处理结果;E.释放连接资源)。
  4. 返回处理结果:将最终的处理结果返回。

Mybatis源码剖析

传统⽅式源码剖析

源码剖析 - 初始化

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

进⼊源码分析:

// 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);
    }
}

MyBatis 在初始化的时候,会将 MyBatis 的配置信息全部加载到内存中,使⽤ org.apache.ibatis.session.Configuration 实例来维护。

配置⽂件解析部分

Configuration 对象介绍:Configuration 对象的结构和 xml 配置⽂件的对象⼏乎相同。xml 中的核心配置标签:properties (属性)、settings (设置)、typeAliases (类型别名)、typeHandlers (类型处理器)、objectFactory (对象⼯⼚)、mappers (映射器) 等,Configuration 也有对应的对象属性来封装它们。

初始化配置⽂件信息的本质:创建 Configuration 对象,将解析的 xml 数据封装到 Configuration 内部属性中。

/**
 * 解析 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);
        // 解析 <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);
    }
}
MappedStatement 介绍
  • 作⽤:MappedStatement 与 Mapper 配置⽂件中的⼀个 select/update/insert/delete 节点相对应。mapper 中配置的标签都被封装到了此对象中,主要⽤途是描述⼀条 SQL 语句。
  • 初始化过程:加载配置⽂件时,会解析 mybatis-config.xml 中的 mappers 标签(引⼊ mapper.xml 或配置 mapper 接⼝⽬录)。例如:
<select id="getUser" resultType="user" > 
    select * from user where id=#{id} 
</select>

该 select 标签会被解析封装成 MappedStatement 对象,存储在 Configuration 对象的mappedStatements属性中(HashMap结构,key = 全限定类名 + ⽅法名,value=MappedStatement 对象)。

Configuration 中对应的属性:

Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

XMLConfigBuilder 中的处理:

private void parseConfiguration(XNode root) {
    try {
        //省略其他标签的处理
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause:" + e, e);
    }
}
回到 build 重载⽅法
// 5.调⽤的重载⽅法
public SqlSessionFactory build(Configuration config) {
    //创建了 DefaultSqlSessionFactory 对象,传⼊ Configuration 对象。
    return new DefaultSqlSessionFactory(config);
}

源码剖析 - 执⾏ SQL 流程

SqlSession 介绍

SqlSession 是⼀个接⼝,核心实现类:DefaultSqlSession(默认)、SqlSessionManager(弃⽤)。SqlSession 是 MyBatis 中⽤于和数据库交互的顶层类,通常与 ThreadLocal 绑定,⼀个会话对应⼀个 SqlSession,使⽤完毕需 close。

public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration;
    private final Executor executor;
    // 其他属性和方法...
}

核心参数:

  • configuration:与初始化时的 Configuration 对象相同
  • Executor:执⾏器接⼝,常⽤实现类:
    • BatchExecutor:重⽤语句并执⾏批量更新
    • ReuseExecutor:重⽤预处理语句(prepared statements)
    • SimpleExecutor:普通执⾏器(默认)
获取 SqlSession
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");

源码分析:

//6. 进⼊ openSession ⽅法。
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){
        closeTransaction(tx); // may have fetched a connection so lets call close()
    }
}
执⾏ sqlsession 中的 API
//8.进⼊selectList⽅法,多个重载⽅法。
public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
}

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

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();
    }
}

源码剖析 - Executor

进⼊executor.query()方法:

//此⽅法在SimpleExecutor的⽗类BaseExecutor中实现
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的重载⽅法中
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抽象⽅法
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 void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
        log.debug("Opening JDBC Connection");
    }
    //从连接池获得连接
    connection = dataSource.getConnection();
    if (level != null) {
        connection.setTransactionIsolation(level.getLevel());
    }
}

Executor 的功能和作⽤

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

源码剖析 - StatementHandler

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

  1. 对 JDBC 的 PreparedStatement 对象,通过parameterize(statement)⽅法对占位符?设值;
  2. 通过List query(Statement statement, ResultHandler resultHandler)⽅法执⾏ Statement,并将 ResultSet 封装成 List。
parameterize (statement) 方法实现
public void parameterize(Statement statement) throws SQLException { 
    //使⽤ParameterHandler对象来完成对Statement的设值 
    parameterHandler.setParameters((PreparedStatement) statement);
}

/** 
 * ParameterHandler 类的 setParameters(PreparedStatement ps) 实现
 * 对某⼀个Statement进⾏设置参数
 */
public void setParameters(PreparedStatement ps) throws SQLException {
    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();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    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();
                }
                //设置参数
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
        }
    }
}
query (Statement, ResultHandler) 方法实现
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.<E> handleResultSets(ps);
}
ResultSetHandler.handleResultSets 方法
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);
}

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");
}

初始化时对接⼝的处理

MapperRegistry是 Configuration 中的⼀个属性,内部维护HashMap存放 mapper 接⼝的⼯⼚类(MapperProxyFactory),每个接⼝对应⼀个⼯⼚类。

mappers 配置示例:

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

解析逻辑:

  • 解析到 mapper 配置⽂件时:将增删改查标签封装成 MappedStatement 对象,存⼊mappedStatements
  • 解析到接⼝时:创建接⼝对应的MapperProxyFactory对象,存⼊HashMap(key = 接⼝字节码对象,value=MapperProxyFactory 对象)。

源码剖析 - getMapper ()

//DefaultSqlSession 中的 getMapper
public <T> T getMapper(Class<T> type) {
    return configuration.<T>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) {
    //从 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, MapperMethod> methodCache;
    
    //构造,传⼊了 SqlSession,说明每个session中的代理对象是不同的!
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    //省略部分源码
}

源码剖析 - invoke ()

代理对象调⽤⽅法时,执⾏MapperProxy中的invoke⽅法:

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 if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //重点在这:MapperMethod最终调⽤了执⾏的⽅法 
    return mapperMethod.execute(sqlSession, args); 
}
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;
}

⼆级缓存源码剖析

核心概念

  • ⼆级缓存构建在⼀级缓存之上,查询优先级:⼆级缓存 → ⼀级缓存 → 数据库;
  • ⼆级缓存与具体命名空间(namespace)绑定,⼀个 Mapper 对应⼀个 Cache,相同 Mapper 的 MappedStatement 共⽤一个 Cache;
  • ⼀级缓存与 SqlSession 绑定。

启⽤⼆级缓存

步骤 1:开启全局配置
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
步骤 2:Mapper 中配置 cache 标签
<cache></cache>
步骤 3:CURD 标签配置 useCache=true
<select id="findById" resultType="com.lagou.pojo.User" useCache="true">
    select * from user where id = #{id}
</select>

标签 <cache/> 的解析

核心解析流程
// XMLConfigBuilder.parse()
public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));// 在这⾥
    return configuration;
}

// parseConfiguration()
private void parseConfiguration(XNode root) {
    try {
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        propertiesElement(root.evalNode("properties"));
        loadCustomVfs(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectionFactoryElement(root.evalNode("reflectionFactory"));
        settingsElement(settings);

        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 就是这⾥
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

// mapperElement()
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
                String mapperPackage = child.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
                String resource = child.getStringAttribute("resource");
                String url = child.getStringAttribute("url");
                String mapperClass = child.getStringAttribute("class");
                // 按照我们本例的配置,则直接⾛该if判断
                if (resource != null && url == null && mapperClass == null) {
                    ErrorContext.instance().resource(resource);
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    // ⽣成XMLMapperBuilder,并执⾏其parse⽅法
                    mapperParser.parse();
                } else if (resource == null && url != null && mapperClass == null) {
                    ErrorContext.instance().resource(url);
                    InputStream inputStream = Resources.getUrlAsStream(url);
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                    mapperParser.parse();
                } else if (resource == null && url == null && mapperClass != null) {
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}
解析 Mapper.xml 中的 cache 标签
// XMLMapperBuilder.parse()
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        // 解析mapper属性
        configurationElement(parser.evalNode("/mapper"));
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

// configurationElement()
private void configurationElement(XNode context) {
    try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
        builderAssistant.setCurrentNamespace(namespace);
        cacheRefElement(context.evalNode("cache-ref"));
        // 最终在这⾥看到了关于cache属性的处理
        cacheElement(context.evalNode("cache"));
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        sqlElement(context.evalNodes("/mapper/sql"));
        // 这⾥会将⽣成的Cache包装到对应的MappedStatement
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
}

// cacheElement() 
private void cacheElement(XNode context) throws Exception {
    if (context != null) {
        //解析<cache/>标签的type属性,这⾥我们可以⾃定义cache的实现类,⽐如redisCache,如果没有⾃定义,这⾥使⽤和⼀级缓存相同的PERPETUAL
        String type = context.getStringAttribute("type", "PERPETUAL");
        Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
        String eviction = context.getStringAttribute("eviction", "LRU");
        Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
        Long flushInterval = context.getLongAttribute("flushInterval");
        Integer size = context.getIntAttribute("size");

        boolean readWrite = !context.getBooleanAttribute("readOnly", false);
        boolean blocking = context.getBooleanAttribute("blocking", false);
        Properties props = context.getChildrenAsProperties();
        // 构建Cache对象
        builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
}
构建 Cache 对象
// MapperBuilderAssistant.useNewCache()
public Cache useNewCache(Class<? extends Cache> typeClass,
                         Class<? extends Cache> evictionClass,
                         Long flushInterval,
                         Integer size,
                         boolean readWrite,
                         boolean blocking,
                         Properties props) {
    // 1.⽣成Cache对象
    Cache cache = new CacheBuilder(currentNamespace)
        //这⾥如果我们定义了<cache/>中的type,就使⽤⾃定义的Cache,否则使⽤和⼀级缓存相同的PerpetualCache
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    // 2.添加到Configuration中
    configuration.addCache(cache);
    // 3.并将cache赋值给MapperBuilderAssistant.currentCache
    currentCache = cache;
    return cache;
}
将 Cache 包装到 MappedStatement
// buildStatementFromContext() 
private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
        buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
}

//buildStatementFromContext() 
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            // 每⼀条执⾏语句转换成⼀个MappedStatement
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}

// XMLStatementBuilder.parseStatementNode();
public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    // ... 省略部分代码
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    // ... 省略部分代码
    // 创建MappedStatement对象
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                                        fetchSize, timeout, parameterMap, 
                                        parameterTypeClass, resultMap, resultTypeClass,
                                        resultSetTypeEnum, flushCache, useCache, 
                                        resultOrdered, 
                                        keyGenerator, keyProperty, keyColumn, 
                                        databaseId, langDriver, resultSets);
}

// builderAssistant.addMappedStatement()
public MappedStatement addMappedStatement(
    String id,
    // ... 省略其他参数
) {
    if (unresolvedCacheRef) {
        throw new IncompleteElementException("Cache-ref not yet resolved");
    }
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //创建MappedStatement对象
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        // ... 省略其他配置
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);// 在这⾥将之前⽣成的Cache封装到MappedStatement
    
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
        statementBuilder.parameterMap(statementParameterMap);
    }
    MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
    return statement;
}

查询源码分析(CachingExecutor)

// CachingExecutor 
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建 CacheKey
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    // 从 MappedStatement 中获取 Cache,注意这⾥的 Cache 是从MappedStatement中获取的
    // 也就是我们上⾯解析Mapper中<cache/>标签中创建的,它保存在Configration中
    // 我们在上⾯解析blog.xml时分析过每⼀个MappedStatement都有⼀个Cache对象,就是这⾥
    Cache cache = ms.getCache();
    // 如果配置⽂件中没有配置 <cache>,则 cache 为空
    if (cache != null) {
        //如果需要刷新缓存的话就刷新:flushCache="true"
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            // 访问⼆级缓存
            List<E> list = (List<E>) tcm.getObject(cache, key);
            // 缓存未命中
            if (list == null) {
                // 如果没有值,则执⾏查询,这个查询实际也是先⾛⼀级缓存查询,⼀级缓存也没有的话,则进⾏DB查询
                list = delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                // 缓存查询结果
                tcm.putObject(cache, key, list);
            }
            return list;
        }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

TransactionalCacheManager

/** 事务缓存管理器 */
public class TransactionalCacheManager {
    // Cache 与 TransactionalCache 的映射关系表
    private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();

    public void clear(Cache cache) {
        // 获取 TransactionalCache 对象,并调⽤该对象的 clear ⽅法,下同
        getTransactionalCache(cache).clear();
    }

    public Object getObject(Cache cache, CacheKey key) {
        // 直接从TransactionalCache中获取缓存
        return getTransactionalCache(cache).getObject(key);
    }

    public void putObject(Cache cache, CacheKey key, Object value) {
        // 直接存⼊TransactionalCache的缓存中
        getTransactionalCache(cache).putObject(key, value);
    }

    public void commit() {
        for (TransactionalCache txCache : transactionalCaches.values()) {
            txCache.commit();
        }
    }

    public void rollback() {
        for (TransactionalCache txCache : transactionalCaches.values()) {
            txCache.rollback();
        }
    }

    private TransactionalCache getTransactionalCache(Cache cache) {
        // 从映射表中获取 TransactionalCache
        TransactionalCache txCache = transactionalCaches.get(cache);
        if (txCache == null) {
            // TransactionalCache 也是⼀种装饰类,为 Cache 增加事务功能
            // 创建⼀个新的TransactionalCache,并将真正的Cache对象存进去
            txCache = new TransactionalCache(cache);
            transactionalCaches.put(cache, txCache);
        }
        return txCache;
    }
}

TransactionalCache

public class TransactionalCache implements Cache {
    //真正的缓存对象,和上⾯的Map<Cache, TransactionalCache>中的Cache是同⼀个
    private final Cache delegate;
    private boolean clearOnCommit;
    // 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
    private final Map<Object, Object> entriesToAddOnCommit;
    // 在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中
    private final Set<Object> entriesMissedInCache;

    @Override
    public Object getObject(Object key) {
        // 查询的时候是直接从delegate中去查询的,也就是从真正的缓存对象中查询
        Object object = delegate.getObject(key);
        if (object == null) {
            // 缓存未命中,则将 key 存⼊到 entriesMissedInCache 中
            entriesMissedInCache.add(key);
        }
        if (clearOnCommit) {
            return null;
        } else {
            return object;
        }
    }

    @Override
    public void putObject(Object key, Object object) {
        // 将键值对存⼊到 entriesToAddOnCommit 这个Map中中,⽽⾮真实的缓存对象 delegate 中
        entriesToAddOnCommit.put(key, object);
    }

    @Override
    public Object removeObject(Object key) {
        return null;
    }

    @Override
    public void clear() {
        clearOnCommit = true;
        // 清空 entriesToAddOnCommit,但不清空 delegate 缓存
        entriesToAddOnCommit.clear();
    }

    public void commit() {
        // 根据 clearOnCommit 的值决定是否清空 delegate
        if (clearOnCommit) {
            delegate.clear();
        }
        // 刷新未缓存的结果到 delegate 缓存中
        flushPendingEntries();
        // 重置 entriesToAddOnCommit 和 entriesMissedInCache
        reset();
    }

    public void rollback() {
        unlockMissedEntries();
        reset();
    }

    private void reset() {
        clearOnCommit = false;
        // 清空集合
        entriesToAddOnCommit.clear();
        entriesMissedInCache.clear();
    }

    private void flushPendingEntries() {
        for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
            // 将 entriesToAddOnCommit 中的内容转存到 delegate 中
            delegate.putObject(entry.getKey(), entry.getValue());
        }
        for (Object entry : entriesMissedInCache) {
            if (!entriesToAddOnCommit.containsKey(entry)) {
                // 存⼊空值
                delegate.putObject(entry, null);
            }
        }
    }

    private void unlockMissedEntries() {
        for (Object entry : entriesMissedInCache) {
            try {
                // 调⽤ removeObject 进⾏解锁
                delegate.removeObject(entry);
            } catch (Exception e) {
                log.warn("...");
            }
        }
    }
}

缓存生效时机(SqlSession.commit ())

// SqlSession.commit()
@Override
public void commit(boolean force) {
    try {
        // 主要是这句
        executor.commit(isCommitOrRollbackRequired(force));
        dirty = false;
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

// CachingExecutor.commit()
@Override 
public void commit(boolean required) throws SQLException {
    delegate.commit(required);
    tcm.commit();// 在这⾥
}

// TransactionalCacheManager.commit()
public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
        txCache.commit();// 在这⾥
    }
}

// TransactionalCache.commit()
public void commit() {
    if (clearOnCommit) {
        delegate.clear();
    }
    flushPendingEntries();//这⼀句
    reset();
}

// TransactionalCache.flushPendingEntries()
private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
        // 在这⾥真正的将entriesToAddOnCommit的对象逐个添加到delegate中,只有这时,⼆级缓存才真正的⽣效
        delegate.putObject(entry.getKey(), entry.getValue());
    }

    for (Object entry : entriesMissedInCache) {
        if (!entriesToAddOnCommit.containsKey(entry)) {
            delegate.putObject(entry, null);
        }
    }
}

⼆级缓存的刷新

// SqlSession.update()
public int update(String statement, Object parameter) {
    int var4;
    try {
        this.dirty = true;
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var4 = this.executor.update(ms, this.wrapCollection(parameter));
    } catch (Exception var8) {
        throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
    } finally {
        ErrorContext.instance().reset();
    }
    return var4;
}

// CachingExecutor.update()
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    this.flushCacheIfRequired(ms);
    return this.delegate.update(ms, parameterObject);
}

// CachingExecutor.flushCacheIfRequired()
private void flushCacheIfRequired(MappedStatement ms) {
    //获取MappedStatement对应的Cache,进⾏清空
    Cache cache = ms.getCache();
    //SQL需设置flushCache="true" 才会执⾏清空
    if (cache != null && ms.isFlushCacheRequired()) {
        this.tcm.clear(cache);
    }
}

⼆级缓存总结

  1. 在⼆级缓存的设计上,MyBatis ⼤量地运⽤了装饰者模式,如CachingExecutor、各种Cache接⼝的装饰器;
  2. ⼆级缓存实现了 SqlSession 之间的缓存数据共享,属于 namespace 级别;
  3. ⼆级缓存具有丰富的缓存策略;
  4. ⼆级缓存可由多个装饰器与基础缓存组合⽽成;
  5. ⼆级缓存⼯作由缓存装饰执⾏器CachingExecutor和事务型预缓存TransactionalCache完成。

延迟加载源码剖析

什么是延迟加载?

问题场景

在开发过程中很多时候我们并不需要总是在加载⽤户信息时就⼀定要加载他的订单信息。例如:

  • ⼀对多场景:查询⽤户时,⽤户的 100 个订单不需要立即加载,应该 "什么时候⽤,什么时候查";
  • ⼀对⼀场景:查询订单时,订单所属的⽤户信息应该随订单⼀起查询。
延迟加载定义

延迟加载(懒加载):需要⽤到数据时才进⾏加载,不需要时不加载。

优缺点
  • 优点:先从单表查询,需要时再关联查询,⼤⼤提⾼数据库性能(单表查询⽐多表关联查询快);
  • 缺点:⼤批量数据查询时,按需查询会增加数据库交互次数,可能导致⽤户等待时间变⻓。
适用场景
  • ⼀对多、多对多:通常采⽤延迟加载;
  • ⼀对⼀(多对⼀):通常采⽤⽴即加载;
  • 注意:延迟加载基于嵌套查询实现。

延迟加载实现

局部延迟加载

associationcollection标签中通过fetchType属性设置:

<!-- 开启⼀对多 延迟加载 -->
<resultMap id="userMap" type="user">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
    <result column="birthday" property="birthday"></result>
    <!--
        fetchType="lazy" 懒加载策略
        fetchType="eager" ⽴即加载策略
    -->
    <collection property="orderList" ofType="order" column="id"   
                select="com.lagou.dao.OrderMapper.findByUid" fetchType="lazy">
    </collection>
</resultMap>

<select id="findAll" resultMap="userMap">
    SELECT * FROM `user`
</select>
全局延迟加载

在 Mybatis 核心配置文件中通过setting标签设置:

<settings>
    <!--开启全局延迟加载功能-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>
局部覆盖全局
<!-- 关闭⼀对⼀ 延迟加载 -->
<resultMap id="orderMap" type="order">
    <id column="id" property="id"></id>
    <result column="ordertime" property="ordertime"></result>
    <result column="total" property="total"></result>
    <!--
        fetchType="lazy" 懒加载策略
        fetchType="eager" ⽴即加载策略
    -->
    <association property="user" column="uid" javaType="user"
                 select="com.lagou.dao.UserMapper.findById" fetchType="eager">      
    </association>
</resultMap>

<select id="findAll" resultMap="orderMap">
    SELECT * from orders
</select>

延迟加载原理

核心逻辑

使⽤ CGLIB 或 Javassist(默认)创建⽬标对象的代理对象。当调⽤代理对象的延迟加载属性的 getter 方法时,进入拦截器:

  1. 例如调⽤a.getB().getName(),触发拦截器invoke(...)方法;
  2. 发现a.getB()需要延迟加载,执⾏预存的关联查询 SQL,查询 B 对象;
  3. 调⽤a.setB(b)为 a 对象的 b 属性赋值;
  4. 完成a.getB().getName()的调⽤。

总结:延迟加载通过动态代理实现,拦截指定方法并按需加载数据。

延迟加载源码剖析

Setting 配置加载
public class Configuration {
    /** 
     * aggressiveLazyLoading:
     * 当开启时,任何⽅法的调⽤都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).
     * 默认为true
     */
    protected boolean aggressiveLazyLoading;

    /**
     * 延迟加载触发⽅法
     */
    protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));

    /** 是否开启延迟加载 */
    protected boolean lazyLoadingEnabled = false;

    /**
     * 默认使⽤Javassist代理⼯⼚
     * @param proxyFactory
     */
    public void setProxyFactory(ProxyFactory proxyFactory) {
        if (proxyFactory == null) {
            proxyFactory = new JavassistProxyFactory();
        }
        this.proxyFactory = proxyFactory;
    }
    
    //省略...
}
延迟加载代理对象创建

Mybatis 的查询结果由ResultSetHandlerhandleResultSets()方法处理,核心实现类为DefaultResultSetHandler

//#mark 创建结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();
    //#mark 创建返回的结果映射的真实对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);

    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
            // 判断属性有没配置嵌套查询,如果有就创建代理对象
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                //#mark 创建延迟加载代理对象
                resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
                break;
            }
        }
    }
    this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
    return resultObject;
}
JavassistProxyFactory 实现
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
    /**
     * 接⼝实现
     * @param target ⽬标结果对象
     * @param lazyLoader 延迟加载对象
     * @param configuration 配置
     * @param objectFactory 对象⼯⼚
     * @param constructorArgTypes 构造参数类型
     * @param constructorArgs 构造参数值
     * @return
     */
    @Override
    public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
    }

    //省略...

    /**
     * 代理对象实现,核⼼逻辑执⾏
     */
    private static class EnhancedResultObjectProxyImpl implements MethodHandler {
        /**
         * 创建代理对象
         * @param type
         * @param callback
         * @param constructorArgTypes
         * @param constructorArgs
         * @return
         */
        static Object createProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
            ProxyFactory enhancer = new ProxyFactory();
            enhancer.setSuperclass(type);
            try {
                //通过获取对象⽅法,判断是否存在该⽅法
                type.getDeclaredMethod(WRITE_REPLACE_METHOD);
                // ObjectOutputStream will call writeReplace of objects returned by writeReplace
                if (log.isDebugEnabled()) {
                    log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
                }
            } catch (NoSuchMethodException e) {
                //没找到该⽅法,实现接⼝
                enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
            } catch (SecurityException e) {
                // nothing to do here
            }

            Object enhanced;
            Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
            Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
            try {
                //创建新的代理对象
                enhanced = enhancer.create(typesArray, valuesArray);
            } catch (Exception e) {
                throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
            }

            //设置代理执⾏器
            ((Proxy) enhanced).setHandler(callback);
            return enhanced;
        }

        /**
         * 代理对象执⾏
         * @param enhanced 原对象
         * @param method 原对象⽅法
         * @param methodProxy 代理⽅法
         * @param args ⽅法参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
            final String methodName = method.getName();
            try {
                synchronized (lazyLoader) {
                    if (WRITE_REPLACE_METHOD.equals(methodName)) { 
                        //忽略暂未找到具体作⽤
                        Object original;
                        if (constructorArgTypes.isEmpty()) {
                            original = objectFactory.create(type);
                        } else {
                            original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                        }
                        PropertyCopier.copyBeanProperties(type, enhanced, original);
                        if (lazyLoader.size() > 0) {
                            return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                        } else {
                            return original;
                        }
                    } else {
                        //延迟加载数量⼤于0
                        if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                            //aggressive ⼀次加载所有需要延迟加载属性或者包含触发延迟加载⽅法
                            if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                                log.debug("==> laze load trigger method:" + methodName + ",proxy method:" + methodProxy.getName() + " class:" + enhanced.getClass());
                                //⼀次全部加载
                                lazyLoader.loadAll();
                            } else if (PropertyNamer.isSetter(methodName)) {
                                //判断是否为set⽅法,set⽅法不需要延迟加载
                                final String property = PropertyNamer.methodToProperty(methodName);
                                lazyLoader.remove(property);
                            } else if (PropertyNamer.isGetter(methodName)) {
                                final String property = PropertyNamer.methodToProperty(methodName);
                                if (lazyLoader.hasLoader(property)) {
                                    //延迟加载单个属性
                                    lazyLoader.load(property);
                                    log.debug("load one :" + methodName);
                                }
                            }
                        }
                    }
                }
            }
            return methodProxy.invoke(enhanced, args);
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }
}

注意事项

IDEA 调试问题:当配置aggressiveLazyLoading=true时,IDEA 的 Debuger 窗体会触发延迟加载对象的方法,导致延迟加载代码无法进入调试。核心原因是aggressiveLazyLoading开启后,任何方法调用都会加载所有属性。

设计模式

虽然我们都知道有 3 类 23 种设计模式,但大多停留在概念层面。MyBatis 源码中大量使用了设计模式,观察其应用能更深入理解设计模式。

MyBatis 至少用到了以下设计模式:

模式

MyBatis 体现

建造者模式

例如SqlSessionFactoryBuilderEnvironment

工厂方法模式

例如SqlSessionFactoryTransactionFactoryLogFactory

单例模式

例如ErrorContextLogFactory

代理模式

MyBatis 实现的核心,比如MapperProxyConnectionLogger(用 JDK 动态代理);executor.loader包使用 cglib 或 javassist 实现延迟加载

组合模式

例如SqlNode及各个子类(如ChooseSqlNode等);

模板方法模式

例如BaseExecutorSimpleExecutor,还有BaseTypeHandler及所有子类(如IntegerTypeHandler);

适配器模式

例如Log的 MyBatis 接口,及它对 jdbc、log4j 等日志框架的适配实现;

装饰者模式

例如Cache包中cache.decorators子包下各个装饰者的实现;

迭代器模式

例如PropertyTokenizer

接下来对建造者模式、工厂模式、代理模式进行解读:先介绍模式自身知识,再解读其在 MyBatis 中的应用。

11.1 Builder 构建者模式

模式定义

Builder 模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。属于创建类模式。

当一个对象的构建逻辑复杂(超出构造函数的能力范围)时,可使用 Builder 模式:通过多个简单对象逐步组装成复杂对象。

示例:用建造者模式生产 Computer

1. 定义目标类(Computer)
package com.lagou.dao;
public class Computer {
    private String displayer;
    private String mainUnit;
    private String mouse;
    private String keyboard;

    // getter、setter
    public String getDisplayer() { return displayer; }
    public void setDisplayer(String displayer) { this.displayer = displayer; }
    public String getMainUnit() { return mainUnit; }
    public void setMainUnit(String mainUnit) { this.mainUnit = mainUnit; }
    public String getMouse() { return mouse; }
    public void setMouse(String mouse) { this.mouse = mouse; }
    public String getKeyboard() { return keyboard; }
    public void setKeyboard(String keyboard) { this.keyboard = keyboard; }

    @Override
    public String toString() {
        return "Computer{" +
                "displayer='" + displayer + '\'' +
                ", mainUnit='" + mainUnit + '\'' +
                ", mouse='" + mouse + '\'' +
                ", keyboard='" + keyboard + '\'' +
                '}';
    }
}
2. 创建构建类(ComputerBuilder)
public static class ComputerBuilder {
    private Computer target = new Computer();

    public ComputerBuilder installDisplayer(String displayer) {
        target.setDisplayer(displayer);
        return this;
    }
    public ComputerBuilder installMainUnit(String mainUnit) {
        target.setMainUnit(mainUnit);
        return this;
    }
    public ComputerBuilder installMouse(String mouse) {
        target.setMouse(mouse);
        return this;
    }
    public ComputerBuilder installKeyboard(String keyboard) {
        target.setKeyboard(keyboard);
        return this;
    }
    public Computer build() {
        return target;
    }
}
3. 调用构建类
public static void main(String[] args) {
    ComputerBuilder computerBuilder = new ComputerBuilder();
    Computer computer = computerBuilder
            .installDisplayer("显示器")
            .installMainUnit("主机")
            .installKeyboard("键盘")
            .installMouse("鼠标")
            .build();
    System.out.println(computer);
}

MyBatis 中的体现:SqlSessionFactory 的构建

MyBatis 初始化逻辑复杂,无法通过单一构造函数完成,因此大量使用 Builder 模式分层构建,核心对象ConfigurationXmlConfigBuilder负责构造。

构建流程

SqlSessionFactoryBuilder调用XMLConfigBuilder读取mybatis-config.xml和所有*Mapper.xml,构建核心对象Configuration,再以Configuration为参数构建SqlSessionFactory

// XMLConfigBuilder解析配置的核心方法
private void parseConfiguration(XNode root) {
    try {
        // 解析<properties />标签
        propertiesElement(root.evalNode("properties"));
        // 解析<settings />标签
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        // 加载自定义VFS实现类
        loadCustomVfs(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);
        // 解析<environments />标签
        environmentsElement(root.evalNode("environments"));
        // 解析<databaseIdProvider />标签
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        // 解析<mappers />标签
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
分层 Builder 的协作
  • XMLConfigBuilder构建Configuration时,会调用XMLMapperBuilder读取*Mapper.xml
  • XMLMapperBuilder会调用XMLStatementBuilder解析并构建所有 SQL 语句。

这些 Builder 负责文件读取、语法解析、反射生成对象、缓存结果等复杂逻辑,是单一构造函数无法覆盖的,因此必须用 Builder 模式拆分逻辑。

11.2 ⼯⼚模式

模式定义

简单工厂模式(静态工厂方法模式):定义一个类专门负责创建其他类的实例,被创建的实例通常具有共同父类。属于创建型模式。

示例:生产电脑

1. 抽象产品类(Computer)
public abstract class Computer {
    /** 抽象方法,由具体产品类实现 */
    public abstract void start();
}
2. 具体产品类
// 联想电脑
public class LenovoComputer extends Computer {
    @Override
    public void start() {
        System.out.println("联想电脑启动");
    }
}

// 惠普电脑
public class HpComputer extends Computer {
    @Override
    public void start() {
        System.out.println("惠普电脑启动");
    }
}
3. 工厂类
public class ComputerFactory {
    public static Computer createComputer(String type) {
        Computer computer = null;
        switch (type) {
            case "lenovo":
                computer = new LenovoComputer();
                break;
            case "hp":
                computer = new HpComputer();
                break;
        }
        return computer;
    }
}
4. 客户端调用
public class CreatComputer {
    public static void main(String[] args) {
        ComputerFactory.createComputer("hp").start();
    }
}

MyBatis 中的体现:SqlSession 的创建

MyBatis 中SqlSession(执行 SQL、管理事务的核心接口)的创建使用了简单工厂模式,由SqlSessionFactory负责生产SqlSession

工厂实现(DefaultSqlSessionFactory)
private SqlSession openSessionFromDataSource(ExecutorType execType,
                                             TransactionIsolationLevel level,
                                             boolean autoCommit) {
    Transaction tx = null;
    try {
        // 从Configuration读取环境配置
        final Environment environment = configuration.getEnvironment();
        // 获取TransactionFactory
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        // 创建Transaction对象
        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) {
        closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}
逻辑说明

SqlSessionFactoryopenSession()方法支持多种参数(如autoCommitExecutorType),通过工厂方法统一组装ConfigurationExecutor、事务参数,最终生成SqlSession对象。

11.3 代理模式

模式定义

代理模式:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。分为静态代理和动态代理(MyBatis 主要用动态代理)。

示例:JDK 动态代理

1. 抽象接口(Person)
public interface Person {
    void doSomething();
}
2. 具体实现类(Bob)
public class Bob implements Person {
    @Override
    public void doSomething() {
        System.out.println("Bob doing something!");
    }
}
3. 动态代理类(JDKDynamicProxy)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler {
    // 被代理的对象
    private Person target;

    public JDKDynamicProxy(Person person) {
        this.target = person;
    }

    // 获取代理对象
    public Person getProxy() {
        return (Person) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }

    // 代理逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDKDynamicProxy do something before!");
        Object result = method.invoke(target, args);
        System.out.println("JDKDynamicProxy do something after!");
        return result;
    }
}
4. 测试类
public class JDKDynamicTest {
    public static void main(String[] args) {
        System.out.println("不使用代理类:");
        Person person = new Bob();
        person.doSomething();

        System.out.println("分割线-----------");
        System.out.println("使用代理类:");
        Person proxyPerson = new JDKDynamicProxy(new Bob()).getProxy();
        proxyPerson.doSomething();
    }
}

MyBatis 中的体现:Mapper 接口的动态代理

MyBatis 的核心特性之一:只需编写Mapper.java接口(无需实现类),由 MyBatis 动态生成代理类执行 SQL。

代理对象的生成

当调用Configuration.getMapper()时,会通过MapperRegistry获取MapperProxyFactory,再由工厂生成代理对象:

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    // 生成代理对象
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(
                mapperInterface.getClassLoader(),
                new Class[]{mapperInterface},
                mapperProxy
        );
    }
}
代理逻辑的执行(MapperProxy)

MapperProxy实现了InvocationHandler接口,所有 Mapper 接口的方法调用都会转发到invoke()方法:

public class MapperProxy<T> implements InvocationHandler, Serializable {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是Object类的方法(如toString),直接执行
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 获取MapperMethod并执行SQL
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
}
逻辑说明

通过动态代理,MyBatis 将Mapper接口方法的调用,转发为SqlSession的 SQL 执行逻辑(如sqlSession.selectOne()),从而实现 “只写接口、不写实现” 的特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值