大佬教你进阶篇-Mybatis源码分析

1、mybatis 整体功能架构

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

1.jpg

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

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

组件名称组件说明
Executor框架核心调度执行器组件,主要用来调用生成sql语句和缓存维护,主要有两个实现:BaseExecutor、CachingExecutor
StatementHandler用来封装jdbc statement的相关操作,设置参数、结果映射等,主要实现有BaseStatementHandler、CallableStatementHandler、PreparedStatementHandler、SimpleStatementHanlder、RoutingStatementHandler。
ParameterHandler主要用来处理传入参数到statement所需参数的映射
ResultSetHandler主要用来将jdbc返回结果映射到具体的List集合,完成从数据库类型到程序实体类的转化,默认实现为DefaultResultSethandler

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

流程图.png

4、mybatis 核心类说明

类名类描述
SqlSessionFactoryBuilderSqlSessionFactory构造器,构建者模式,使用build方法,可根据输入流InputStream、Reader等进行构建
SqlSessionFactorySqlSession工厂类,用于创建SqlSession, 传入参数Configuration
SqlSessionSqlSession代码一次sql执行会话,里面携带着全局配置信息
Configurationmybatis 全局配置管理对象,包含mybatis所有的配置信息,包括数据源和所有的xml文件信息
MappedStatement和一个insert|update|delete|select 标签对应的对象,用来存储一个sql语句片段信息
SqlSource用来针对不同类型的sql语句生成最终的可执行的sql语句,主要实现有DynamicSqlSource、RawSqlSource、StaticSqlSource、MixedSqlSource
BoundSql用来将sql语句片段和参数进行绑定存储,携带着sql语句和参数信息
TypeHandler用来完成java类型和jdbc数据类型的转换
XMLConfigBuilder主要用来解析全局配置信息,如:environment, dataSource等信息,并封装到Configuration中
XMLMapperBuilder主要用来解析xml文件中的select|insert|update|delete标签,并封装成MappedStatement存储到Configuration中
XMLScriptBuilder主要用来解析将sql语句中的各个动态标签及动态sql语句,解析和参数映射后,最终构造成StaticSqlSource输出保存可执行的完整sql语句
XPathParser主要用来解析配置文件,形成可基于dom操作的Document文档对象
ParameterMapping用来保存sql中动态参数名称和参数值的映射关系的对象

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资料的可以通过扫一扫领取

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值