Mybatis 源码分析(一)配置文件加载流程

1、项目构建

引入依赖

    <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.48</version></dependency>

创建mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><!--配置DAO层位置(mapper.xml对应接口路径)--><package name="com.xiaofeizhu.dao"/></typeAliases><environments default="mysql"><environment id="mysql"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="url" value="jdbc:mysql://127.0.0.1:3306/xiaofeizhu"/><property name="driver" value="com.mysql.jdbc.Driver"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><!--   mapper.xml文件路径--><mapper resource="mapper/UserInfoDao.xml" /></mappers></configuration>

2、配置文件对象创建

2.1 概念说明

  1. Java是面向对象的编程语言,即万物皆可对象的理念,所以配置文件在Java必然会被视为对象;
  2. ORM:对象-表-关系的映射 【mybatis】
  3. OXM:对象-XML文件-关系 【XPath:XPath 是一门在 XML 文档中查找信息的语言】

2.2 Mybatis如何处理配置文件

结论:Mybatis会将配置文件映射成Configuration对象

方式: Mybatis会通过XPathParser解释器去解析配置文件

2.3 Mybatis创建Configuration对象代码跟踪

package com.xiaofeizhu;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
public class Test {/*** Mybatis读取配置文件创建SQLSession* @param args* @throws IOException*/public static void main(String[] args) throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//【代码分析】 这里的build()方法回去加载配置文件的输入流,所以可以断定配置文件对象的创建发生在这个方法,进一步跟踪build()方法SqlSession sqlSession = sqlSessionFactory.openSession();}}
//跟踪进入的build()方法
public SqlSessionFactory build(InputStream inputStream) {return this.build((InputStream)inputStream, (String)null, (Properties)null);}//实际指向的重载的build()方法public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {SqlSessionFactory var5;try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);var5 = this.build(parser.parse());//【代码分析】 可以看到inputStream输入流实际用来创建了XMLConfigBuilder对象parser,之后调用了parser.parse()方法,所以进一步跟踪parse()方法} catch (Exception var14) {throw ExceptionFactory.wrapException("Error building SqlSession.", var14);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException var13) {}}return var5;}
    public Configuration parse() {if (this.parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");} else {this.parsed = true;this.parseConfiguration(this.parser.evalNode("/configuration"));return this.configuration;//【代码分析】 这里可以看到这个parse()方法返回的是Configuration对象,所以可以判断mybatis-config.xml文件被映射成Configuration对象是发生在这一步,需要进一步跟踪this.parseConfiguration(this.parser.evalNode("/configuration"))里发生了什么//【代码说明】 this.parseConfiguration(this.parser.evalNode("/configuration"));这里的this.parser.evalNode("/configuration")是XPath的语法,意思是加载configuration节点下的所有内容{configuration就是mybatis-config.xml的根标签}}}
private void parseConfiguration(XNode root) {try {this.propertiesElement(root.evalNode("properties"));Properties settings = this.settingsAsProperties(root.evalNode("settings"));this.loadCustomVfs(settings);this.typeAliasesElement(root.evalNode("typeAliases"));this.pluginElement(root.evalNode("plugins"));this.objectFactoryElement(root.evalNode("objectFactory"));this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));this.reflectorFactoryElement(root.evalNode("reflectorFactory"));this.settingsElement(settings);this.environmentsElement(root.evalNode("environments"));this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));this.typeHandlerElement(root.evalNode("typeHandlers"));this.mapperElement(root.evalNode("mappers"));} catch (Exception var3) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);}}//【代码分析1】 上一步跟踪的代码:this.parseConfiguration(this.parser.evalNode("/configuration"));而parseConfiguration(XNode root)里的参数是Xnode对象{mybatis通过XPathParser解释器将myabtis-config.xml对象解析成XNode对象,及各个标签的映射,限于篇幅不做赘述,有兴趣可以跟一下代码或者看一下XPath语法}
//【代码分析2】parseConfiguration()方法里读取XNode对象各个节点的值赋值给Configuration对象里的各个参数
//【重点说明1】 这里出现的各个属性的顺序就是mybatis-config.xml配置文件的顺序,也是为啥配置的时候标签属性有固定的顺序,因为XPathparser是按照定义好的标签顺序去解析的;
//【重点说明2】this.mapperElement(root.evalNode("mappers")); 这个属性的赋值需要注意,这里mapperElement里会进行读取mapper文件的路径解析赋值这样一个过程,需要跟踪一下代码分析
private void mapperElement(XNode parent) throws Exception {if (parent != null) {Iterator var2 = parent.getChildren().iterator();while(true) {while(var2.hasNext()) {XNode child = (XNode)var2.next();String resource;if ("package".equals(child.getName())) {resource = child.getStringAttribute("name");this.configuration.addMappers(resource);} else {resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");XMLMapperBuilder mapperParser;InputStream inputStream;if (resource != null && url == null && mapperClass == null) {//【代码分析】 这里有三种情况对应<mapper/>里的三种类型的属性,譬如这里就是指 <mapper resource="mapper/UserInfoDao.xml" /> 通过resource属性来制定mapper.xml文件路径ErrorContext.instance().resource(resource);inputStream = Resources.getResourceAsStream(resource);mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());mapperParser.parse();//【代码分析】这里可以看到获取mapper.xml文件路径,创建输入流创建对象的过程,类似之前读取mybatis-config.xml创建configuration对象的过程,这里可以跟踪一下mapperParser.parse()方法} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);inputStream = Resources.getUrlAsStream(url);mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());mapperParser.parse();} else {if (resource != null || url != null || mapperClass == null) {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}Class<?> mapperInterface = Resources.classForName(mapperClass);this.configuration.addMapper(mapperInterface);}}}return;}}}
    public void parse() {if (!this.configuration.isResourceLoaded(this.resource)) {this.configurationElement(this.parser.evalNode("/mapper"));//【代码分析】 类似之前读取mapper.xml文件里<mapper>标签下的所有属性内容,跟踪一下configurationElement()方法this.configuration.addLoadedResource(this.resource);configurationElement()方法this.bindMapperForNamespace();}this.parsePendingResultMaps();this.parsePendingCacheRefs();this.parsePendingStatements();}private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace != null && !namespace.equals("")) {this.builderAssistant.setCurrentNamespace(namespace);this.cacheRefElement(context.evalNode("cache-ref"));this.cacheElement(context.evalNode("cache"));this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));this.resultMapElements(context.evalNodes("/mapper/resultMap"));this.sqlElement(context.evalNodes("/mapper/sql"));this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//【代码分析】 这里可以看到这个方法里是在读取mapper.xml文件里各个标签的属性,因为核心处理过程是类似的,这里的最终的目的是将所有的mapper.xml文件封装成MappedStatement对象赋值到Configuration对象的属性(mappedStatements)里,额,具体跟踪就不放在这一步一步跟踪了,后续跟踪的核心的对象,会在Mybatis核心对象里给出【主要太累人,哈哈,偷懒偷懒】} else {throw new BuilderException("Mapper's namespace cannot be empty");}} catch (Exception var3) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);}}
//Configuration对象属性【对应着myabtis-config.xml各个标签的值】
public class Configuration {protected Environment environment;protected boolean safeRowBoundsEnabled;protected boolean safeResultHandlerEnabled;protected boolean mapUnderscoreToCamelCase;protected boolean aggressiveLazyLoading;protected boolean multipleResultSetsEnabled;protected boolean useGeneratedKeys;protected boolean useColumnLabel;protected boolean cacheEnabled;protected boolean callSettersOnNulls;protected boolean useActualParamName;protected boolean returnInstanceForEmptyRow;protected String logPrefix;protected Class<? extends Log> logImpl;protected Class<? extends VFS> vfsImpl;protected LocalCacheScope localCacheScope;protected JdbcType jdbcTypeForNull;protected Set<String> lazyLoadTriggerMethods;protected Integer defaultStatementTimeout;protected Integer defaultFetchSize;protected ExecutorType defaultExecutorType;protected AutoMappingBehavior autoMappingBehavior;protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;protected Properties variables;protected ReflectorFactory reflectorFactory;protected ObjectFactory objectFactory;protected ObjectWrapperFactory objectWrapperFactory;protected boolean lazyLoadingEnabled;protected ProxyFactory proxyFactory;protected String databaseId;protected Class<?> configurationFactory;protected final MapperRegistry mapperRegistry;protected final InterceptorChain interceptorChain;protected final TypeHandlerRegistry typeHandlerRegistry;protected final TypeAliasRegistry typeAliasRegistry;protected final LanguageDriverRegistry languageRegistry;protected final Map<String, MappedStatement> mappedStatements;protected final Map<String, Cache> caches;protected final Map<String, ResultMap> resultMaps;protected final Map<String, ParameterMap> parameterMaps;protected final Map<String, KeyGenerator> keyGenerators;protected final Set<String> loadedResources;protected final Map<String, XNode> sqlFragments;protected final Collection<XMLStatementBuilder> incompleteStatements;protected final Collection<CacheRefResolver> incompleteCacheRefs;protected final Collection<ResultMapResolver> incompleteResultMaps;protected final Collection<MethodResolver> incompleteMethods;protected final Map<String, String> cacheRefMap;
}
//MappedStatement对象属性【对应着mapper.xml文件各个标签属性】
public final class MappedStatement {private String resource;private Configuration configuration;private String id;private Integer fetchSize;private Integer timeout;private StatementType statementType;private ResultSetType resultSetType;private SqlSource sqlSource;private Cache cache;private ParameterMap parameterMap;private List<ResultMap> resultMaps;private boolean flushCacheRequired;private boolean useCache;private boolean resultOrdered;private SqlCommandType sqlCommandType;private KeyGenerator keyGenerator;private String[] keyProperties;private String[] keyColumns;private boolean hasNestedResultMaps;private String databaseId;private Log statementLog;private LanguageDriver lang;private String[] resultSets;
}

Mybatis 源码分析(一)配置文件加载流程相关推荐

  1. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  2. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  3. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

  4. Android 11.0 Settings源码分析 - 主界面加载

    Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...

  5. 从源码解析-结合Activity加载流程深入理解ActivityThrad的工作逻辑

    ActivityThread源码解析 前言 类简称 类简介 一 二 三 四 五 代理和桩的理解 ActivityThread ActivityThread.main AT.attach AMN.get ...

  6. JVM源码阅读-本地库加载流程和原理

    前言 本文主要研究OpenJDK中JVM源码中涉及到native本地库的加载流程和原理的部分.主要目的是为了了解本地库是如何被加载到虚拟机,以及是如何找到并执行本地库里的本地方法,以及JNI的 JNI ...

  7. 【框架源码】Spring源码解析之BeanDefinition加载流程解析

    观看本文之前,我们先思考一个问题,Spring是如何描述Bean对象的? Spring是根据BeanDefinition来创建Bean对象,BeanDefinition就是Spring中表示Bean定 ...

  8. springboot集成mybatis源码分析-启动加载mybatis过程(二)

    springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...

  9. MyBatis 源码分析 - 配置文件解析过程

    文章目录 * 本文速览 1.简介 2.配置文件解析过程分析 2.1 配置文件解析入口 2.2 解析 properties 配置 2.3 解析 settings 配置 2.3.1 settings 节点 ...

最新文章

  1. Uber准备放弃自动驾驶,转手卖给前谷歌无人车CTO,估值曾被孙正义炒到72.5亿美元...
  2. 快速得到两个list中不同部分的list
  3. 究竟该不该“勃”!!!
  4. angular1.0 $http jsonp callback
  5. CYPRESS USB芯片win10驱动
  6. Laravel3 学习笔记
  7. 将 Win32 程序移植到 Linux
  8. Javascript 函数详解
  9. 2021年安徽普通高考考试成绩查询,安徽省教育招生考试院:2021年安徽高考查分入口、查分系统...
  10. java final修改器_Java中的“ final”关键字如何工作?(我仍然可以修改对象。)...
  11. simpledateformat格式_大厂都是怎么用Java8代替SimpleDateFormat?
  12. react html编辑器,在线代码编辑器 Codemirror 的轻量级 React 组件
  13. Python入门--面向过程,面向对象,类与对象
  14. 【银河麒麟操作系统安装win字体库】
  15. android 仿站小工具,仿站小工具
  16. 贪心科技机器学习训练营(九)
  17. 操作系统 实时调度
  18. Kettle【实践 01】Linux环境下使用Azkaban定时调用Kettle的KJB或KTR脚本实现自动化数据处理(完整流程实例分享:包含sql+ktr+shell+flow相关文件云资源)
  19. 无穷小微积分吁呼中俄数学中心成立!
  20. 电脑计算机word2007的介绍,word2007电脑版

热门文章

  1. Activiti 工作流变量的修改方法
  2. 8.2-HSRP 第二次:重新搭建拓扑 //IOU
  3. GitHub练习——如何将本地已有项目添加到github
  4. Nginx中间件web服务安装
  5. flex 错误信息类型及解决方法
  6. (How to)Windows Live Writer插入Latex公式(补充)
  7. PHP数据库调用类调用实例
  8. 学以致用 知行合一 ——《产品管理与研发项目管理》课程有感
  9. ESX Server硬件升级步骤
  10. 一位校长写给大学学生的一封信