吃透MyBatis持久层框架设计思路,手撕核心源码,跟着源码核心流程进行切入分析

1、mybatis 整体功能架构

​ mybatis 主要分为三层,从上到下依次为:接口层、数据处理层、基础支撑层,架构图如下所示:

1.jpg

2、mybatis 四大核心JDBC组件简介

​ mybatis 内置核心4大功能组件,分别是Executor、StatementHandler、ParameterHandler、ResultSetHandler,功能说明 如下:

3、mybatis mapper代理工作流程示意图

流程图.png

4、mybatis 核心类说明

5、mybatis 配置初始化部分核心源码

5.1、程序代码入口分析SqlSessionFactoryBuilder#build

SqlSessionFactoryBuilder: 用来初始化SqlSessionFactory, 默认使用DefaultSqlSessionFactory实现

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // XMLConfigBuilder:用来解析XML配置文件
      // 使用构建者模式
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // parser.parse():使用XPATH解析XML配置文件,将配置文件封装为Configuration对象
      // 返回DefaultSqlSessionFactory对象,该对象拥有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.
      }
    }
}

public SqlSessionFactory build(Configuration config) {
    // 创建SqlSessionFactory接口的默认实现类
    return new DefaultSqlSessionFactory(config);
}

5.2、解析全局配置信息ConfigurationXMLConfigBuilder#parse

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

/**
   * 解析XML配置文件
   * @return
   */
public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点
    // 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
        //issue #117 read properties first
        // 解析</properties>标签
        propertiesElement(root.evalNode("properties"));
        // 解析</settings>标签
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        // 解析</typeAliases>标签
        typeAliasesElement(root.evalNode("typeAliases"));
        ....
 		....
      	....
        // 解析</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);
    }
}

6、mybatis mapper文件加载部分核心源码

6.1 解析mapper文件**: **XMLConfigBuilder#mapperElement->XMLMapperBuilder#parse

InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 通过XMLMapperBuilder解析mapper映射文件
mapperParser.parse();

public void parse() {
    // mapper映射文件是否已经加载过
    if (!configuration.isResourceLoaded(resource)) {
        // 从映射文件中的<mapper>根标签开始解析,直到完整的解析完毕
        configurationElement(parser.evalNode("/mapper"));
        // 标记已经解析
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

/**
   * 解析映射文件
   * @param context 映射文件根节点<mapper>对应的XNode
   */
private void configurationElement(XNode context) {
    try {
        // 获取<mapper>标签的namespace值,也就是命名空间
        String namespace = context.getStringAttribute("namespace");
        ....
        ....
        ....
        // 解析<parameterMap>子标签
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 解析<resultMap>子标签
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        // 解析<sql>子标签,也就是SQL片段
        sqlElement(context.evalNodes("/mapper/sql"));
        // 解析<select>\<insert>\<update>\<delete>子标签
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
}

6.2 解析MappedStatement: XMLMapperBuilder#buildStatementFromContext

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        // MappedStatement解析器
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            // 解析select等4个标签,创建MappedStatement对象
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}

MapperBuilderAssistant#addMappedStatement
    
//利用构建者模式,去创建MappedStatement.Builder,用于创建MappedStatement对象
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
    .resource(resource)
    .fetchSize(fetchSize)
    .timeout(timeout)
    .statementType(statementType)
    .keyGenerator(keyGenerator)
    .keyProperty(keyProperty)
    .keyColumn(keyColumn)
    .databaseId(databaseId)
    .lang(lang)
    .resultOrdered(resultOrdered)
    .resultSets(resultSets)
    .resultMaps(getStatementResultMaps(resultMap, resultType, id))
    .resultSetType(resultSetType)
    .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
    .useCache(valueOrDefault(useCache, isSelect))
    .cache(currentCache);

ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
}

// 通过MappedStatement.Builder,构建一个MappedStatement
MappedStatement statement = statementBuilder.build();
// 将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象
configuration.addMappedStatement(statement);

6.3 处理SqlSource: XMLStatementBuilder#parseStatementNode->langDriver.createSqlSource

// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
// 创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

XMLLanguageDriver#createSqlSource
    
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    // 初始化了动态SQL标签处理器
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    // 解析动态SQL
    return builder.parseScriptNode();
}

public SqlSource parseScriptNode() {
    // 解析select\insert\ update\delete标签中的SQL语句,最终将解析到的SqlNode封装到MixedSqlNode中的List集合中
    // ****将带有${}号的SQL信息封装到TextSqlNode
    // ****将带有#{}号的SQL信息封装到StaticTextSqlNode
    // ****将动态SQL标签中的SQL信息分别封装到不同的SqlNode中
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource = null;
    // 如果SQL中包含${}和动态SQL语句,则将SqlNode封装到DynamicSqlSource
    if (isDynamic) {
        sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
        // 如果SQL中包含#{},则将SqlNode封装到RawSqlSource中,并指定parameterType
        sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}

 

最后:

最近我整理了整套《JAVA核心知识点总结》,说实话 ,作为一名Java程序员,不论你需不需要面试都应该好好看下这份资料。拿到手总是不亏的~我的不少粉丝也因此拿到腾讯字节快手等公司的Offer

Java进阶之路群,找管理员获取哦-!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值