图文并茂解析Mybatis配置加载过程!
创始人
2025-05-29 20:31:28

一、Mybatis运行流程概述

为了熟悉Mybatis的运行流程,我们先看一段代码。

public class MybatisDemo {private SqlSessionFactory sqlSessionFactory;@Beforepublic void init() throws IOException {//--------------------第一步:加载配置---------------------------// 1.读取mybatis配置文件创SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);// 1.读取mybatis配置文件创SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);inputStream.close();}@Test// 快速入门public void quickStart() throws IOException {//--------------------第二部,创建代理对象---------------------------// 2.获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取对应mapperTUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//--------------------第三步:获取数据---------------------------// 4.执行查询语句并返回单条数据TUser user = mapper.selectByPrimaryKey(2);System.out.println(user);System.out.println("----------------------------------");// 5.执行查询语句并返回多条数据
//  List users = mapper.selectAll();
//  for (TUser tUser : users) {
//   System.out.println(tUser);
//  }}
}

以上是我们一个使用mybatis访问数据的demo,通过对快速入门代码的分析,可以把 MyBatis 的运行流程分为三大阶段:

  1. 「初始化阶段」:读取 XML 配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作;

  2. 「代理封装阶段」:封装 iBatis 的编程模型,使用 mapper 接口开发的初始化工作;

  3. 「数据访问阶段」:通过 SqlSession 完成 SQL 的解析,参数的映射、SQL 的执行、结果的解析过程;

今天我们就介绍以下第一个阶段中,Mybatis是如何读取配置的

二、配置加载的核心类

建造器三个核心类

在 MyBatis 中负责加载配置文件的核心类有三个,类图如下:

MyBatis加载配置文件核心类

  • BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供的一些通用的方法;

  • XMLConfigBuilder:主要负责解析 mybatis-config.xml;

  • XMLMapperBuilder:主要负责解析映射配置 Mapper.xml 文件;

  • XMLStatementBuilder:主要负责解析映射配置文件中的 SQL 节点;

XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 这三个类在配置文件加载过程中非常重要,具体分工如下图所示:

XMLConfigBuilder

这三个类使用了建造者模式对 configuration 对象进行初始化,但是没有使用建造者模式
的“肉体”(流式编程风格),只用了灵魂(屏蔽复杂对象的创建过程),把建造者模式演绎
成了工厂模式;后面还会对这三个类源码进行分析;

居然这三个对象使用的是建造者模式,那么我们稍后介绍下什么是建造者模式

三、建造者模式

什么是建造者模式

建造者模式(BuilderPattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

建造者模式类图如下:

建造者模式

各要素如下:

  • 「Product」:要创建的复杂对象

  • 「Builder」:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建;

  • 「ConcreteBuilder」:实现 Builder 接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。在建造过程完成后,提供产品的实例;

  • 「Director」:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建;

应用举例:红包的创建是个复杂的过程,可以使用构建者模式进行创建

代码示例:

1、红包对象RedPacket

public class RedPacket {private String publisherName; //发包人private String acceptName; //收包人private BigDecimal packetAmount; //红包金额private int packetType; //红包类型private Date pulishPacketTime; //发包时间private Date openPacketTime; //抢包时间public RedPacket(String publisherName, String acceptName, BigDecimal packetAmount, int packetType, Date pulishPacketTime, Date openPacketTime) {this.publisherName = publisherName;this.acceptName = acceptName;this.packetAmount = packetAmount;this.packetType = packetType;this.pulishPacketTime = pulishPacketTime;this.openPacketTime = openPacketTime;}public String getPublisherName() {return publisherName;}public void setPublisherName(String publisherName) {this.publisherName = publisherName;}public String getAcceptName() {return acceptName;}public void setAcceptName(String acceptName) {this.acceptName = acceptName;}public BigDecimal getPacketAmount() {return packetAmount;}public void setPacketAmount(BigDecimal packetAmount) {this.packetAmount = packetAmount;}public int getPacketType() {return packetType;}public void setPacketType(int packetType) {this.packetType = packetType;}public Date getPulishPacketTime() {return pulishPacketTime;}public void setPulishPacketTime(Date pulishPacketTime) {this.pulishPacketTime = pulishPacketTime;}public Date getOpenPacketTime() {return openPacketTime;}public void setOpenPacketTime(Date openPacketTime) {this.openPacketTime = openPacketTime;}@Overridepublic String toString() {return "RedPacket [publisherName=" + publisherName + ", acceptName="+ acceptName + ", packetAmount=" + packetAmount+ ", packetType=" + packetType + ", pulishPacketTime="+ pulishPacketTime + ", openPacketTime=" + openPacketTime + "]";}
}

2、构建对象

public class Director {public static void main(String[] args) {RedPacket redPacket = RedPacketBuilderImpl.getBulider().setPublisherName("DK").setAcceptName("粉丝").setPacketAmount(new BigDecimal("888")).setPacketType(1)                       .setOpenPacketTime(new Date()).setPulishPacketTime(new Date()).build();System.out.println(redPacket);}
}

PS:流式编程风格越来越流行,如 zookeeper 的 Curator、JDK8 的流式编程等等都是例子。流式编程的优点在于代码编程性更高、可读性更好,缺点在于对程序员编码要求更高、不太利于调试。建造者模式是实现流式编程风格的一种方式;

                                                 与工厂模式区别

建造者模式应用场景如下:

  • 需要生成的对象具有复杂的内部结构,实例化对象时要屏蔽掉对象代码与复杂对象的实例化过程解耦,可以使用建造者模式;简而言之,如果“遇到多个构造器参数时要考虑用构建器”;

  • 对象的实例化是依赖各个组件的产生以及装配顺序,关注的是一步一步地组装出目标对

  • 象,可以使用建造器模式;

建造者模式与工程模式的区别在于:

「设计模式」「形象比喻」「对象复杂度」「客户端参与程度」
工厂模式生产大众版关注的是一个产品整体,无须关心产品的各部分是如何创建出来的;客户端对产品的创建过程参与度低,对象实例化时属性值相对比较固定;
建造者模式生产定制版建造的对象更加复杂,是一个复合产品,它由各个部件复合而成,部件不同产品对象不同,生成的产品粒度细;客户端参与了产品的创建,决定了产品的类型和内容,参与度高;适合实例化对象时属性变化频繁的场景;

四、Configuration 对象介绍

实例化并初始化 Configuration 对象是第一个阶段的最终目的,所以熟悉 configuration 对
象是理解第一个阶段代码的核心;configuration 对象的关键属性解析如下:

  • MapperRegistry:mapper 接口动态代理工厂类的注册中心。在 MyBatis 中,通过mapperProxy 实现 InvocationHandler 接口,MapperProxyFactory 用于生成动态代理的实例对象;

  • ResultMap:用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id,result 等子元素;

  • MappedStatement:用于存储 mapper.xml 文件中的 select、insert、update 和 delete 节点,同时还包含了这些节点的很多重要属性;

  • SqlSource:用于创建 BoundSql,mapper.xml 文件中的 sql 语句会被解析成 BoundSql 对象,经过解析 BoundSql 包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

Configuration对象图解:

Configuration对象图解

需要特别注意的是 Configuration 对象在 MyBatis 中是单例的,生命周期是应用级的,换句话说只要 MyBatis 运行 Configuration 对象就会独一无二的存在;在 MyBatis 中仅在
org.apache.ibatis.builder.xml.XMLConfigBuilder.XMLConfigBuilder(XPathParser, String, Properties)中有实例化 configuration 对象的代码,如下图:

                                 XMLConfigBuilder.XMLConfigBuilder

Configuration 对象的初始化(属性复制),是在建造 SqlSessionfactory 的过程中进行的,接下
来分析第一个阶段的内部流程;

 

五、配置加载流程解析

配置加载过程

可以把第一个阶段配置加载过程分解为四个步骤,四个步骤如下图:

MyBatis配置加载过程

第一步:通过 SqlSessionFactoryBuilder 建造 SqlSessionFactory,并创建 XMLConfigBuilder 对象读取 MyBatis 核心配置文件 , 见源码方法 :
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(Reader, String, Properties)

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {//读取配置文件XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);return build(parser.parse());//解析配置文件得到configuration对象,并返回SqlSessionFactory} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {reader.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}
}

第二步:进入 XMLConfigBuilder 的 parseConfiguration 方法,对 MyBatis 核心配置文件的各个元素进行解析,读取元素信息后填充到 configuration 对象。在 XMLConfigBuilder 的 mapperElement()方法中通过 XMLMapperBuilder 读取所有 mapper.xml 文件;见方法:
org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode)

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {//issue #117 read properties first//解析节点propertiesElement(root.evalNode("properties"));//解析节点Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);//解析节点typeAliasesElement(root.evalNode("typeAliases"));//解析节点pluginElement(root.evalNode("plugins"));//解析节点objectFactoryElement(root.evalNode("objectFactory"));//解析节点objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//解析节点reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);//将settings填充到configuration// 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);}

第三步:XMLMapperBuilder 的核心方法为 configurationElement(XNode),该方法对 mapper.xml 配置文件的各个元素进行解析,读取元素信息后填充到 configuration 对象。

private void configurationElement(XNode context) {try {//获取mapper节点的namespace属性String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}//设置builderAssistant的namespace属性builderAssistant.setCurrentNamespace(namespace);//解析cache-ref节点cacheRefElement(context.evalNode("cache-ref"));//重点分析 :解析cache节点----------------1-------------------cacheElement(context.evalNode("cache"));//解析parameterMap节点(已废弃)parameterMapElement(context.evalNodes("/mapper/parameterMap"));//重点分析 :解析resultMap节点----------------2-------------------resultMapElements(context.evalNodes("/mapper/resultMap"));//解析sql节点sqlElement(context.evalNodes("/mapper/sql"));//重点分析 :解析select、insert、update、delete节点 ----------------3-------------------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);}}

在 XMLMapperBuilder 解析过程中,有四个点需要注意:

  1. resultMapElements(List)方法用于解析 resultMap 节点,这个方法非常重要, 一定要跟源码理解;解析完之后数据保存在 configuration 对象的 resultMaps 属性中;如下图

XMLMapperBuilder 解析过程

  1. 2XMLMapperBuilder 中在实例化二级缓存(见 cacheElement(XNode))、实例化 resultMap (见 resultMapElements(List))过程中都使用了建造者模式,而且是建造者模 式的典型应用;

  2. XMLMapperBuilder 和 XMLMapperStatmentBuilder 有 自 己 的 “ 秘 书 ” MapperBuilderAssistant。XMLMapperBuilder 和 XMLMapperStatmentBuilder 负责解析 读取配置文件里面的信息,MapperBuilderAssistant 负责将信息填充到 configuration。将文件解析和数据的填充的工作分离在不同的类中,符合单一职责原则;

  3. 在 buildStatementFromContext(List)方法中,创建 XMLStatmentBuilder 解析 Mapper.xml 中 select、insert、update、delete 节点

第四步:在 XMLStatmentBuilder 的 parseStatementNode()方法中,对 Mapper.xml 中 select、 insert、update、delete 节点进行解析,并调用 MapperBuilderAssistant 负责将信息填充到 configuration。在理解 parseStatementNod()方法之前,有必要了解 MappedStatement,这个类用于封装 select、insert、update、delete 节点的信息;如下图所示:

XMLStatmentBuilder

至此,整个Mybatis的配置即加载完毕,整个加载流程图如下:

                                    Mybatis的配置加载流程图

 

 

 

相关内容

热门资讯

中煤能源目标涨幅超35%,万华... 4日,券商给予上市公司目标价共7次,按当日收盘价计算,目标价涨幅排名居前的公司有中煤能源、三利谱、新...
长安汽车母公司兵装集团分立,汽... 伴随军品、民品分立,同时并入新成立的汽车央企,有助于长安汽车的聚焦业务,专业发展,为后续与其他汽车央...
恒指半日涨0.42% 恒指半日... 【恒指半日涨0.42%】截至午间收盘,香港恒生指数涨0.42%,恒生科技指数涨0.92%。阿里巴巴、...
“苏超”引爆A股,足球概念持续... 2025.06.05本文字数:1072,阅读时长大约2分钟6月5日,A股三大指数早盘高开后窄幅震荡。...
中国商超的河南现象…… 河南中... 前言:在中国波澜壮阔的零售版图上,河南——这个人口大省、农业大省、交通枢纽——悄然孕育出一种独特的商...
经国务院批准,成立一家新央企 ... 编辑丨余晖据新华财经报道,涉及东风公司和长安汽车的汽车央企重组又有进展。6月5日早间,东风股份、长安...
双碳ETF:6月4日融资买入1... 证券之星消息,6月4日,双碳ETF(561190)融资买入13.81万元,融资偿还18.84万元,融...
广发银行到了最危险的时刻 来源:妙投APP 作者:段明珠 2024年广发银行出现近17年来首次营收净利 “双降”,营收692....
大模型裹挟云大厂重归To G 文 | 科技新知 AI新科技组,作者 | 茯神 ,编辑 | 思原、九黎 2019年10月,微软打败...
安联锐视:6月4日融资买入22... 证券之星消息,6月4日,安联锐视(301042)融资买入229.49万元,融资偿还364.93万元,...
全域创新,新场景塑造美好生活 编者按: 上世纪,杨浦是上海著名的工业城区,工业产值一度占全市五分之一。如今,这个五分之一指向了数字...
算力与电力协同推进!数据ETF... 截至2025年6月5日 11点11分,中证大数据产业指数(930902)上涨0.67%,成分股博思软...
东风、长安合并中止,知情人士:... 作 者丨巩兆恩 易思琳 郑植文编 辑丨吴晓宇 江佩佩视 频丨柳润瑛 许婷婷东风、长安合并重组意外中止...
券商员工实施“老鼠仓”获利20... 本报(chinatimes.net.cn)记者张玫 帅可聪 北京报道近日,中信证券前信息技术中心高级...
卷低价是饮鸩止渴!工信部、中汽... 本文来源:时代周报 作者:武凯车卖得越来越多,钱赚得越来越少?汽车行业“价格战”仍未熄火,多方下场加...
整体消费复苏不及预期,杭州解百... 本报记者 李贝贝 上海报道6月3日上午,杭州解百集团股份有限公司(下称“杭州解百”,600814.S...
四环生物高层换血背后:原实控人... 本报(chinatimes.net.cn)记者于娜 北京报道四环生物,这家在资本市场上曾经备受瞩目的...
市场整体维持窄幅波动格局 如何... 解锁【第一财经智享会员】实时解读市场动态,把握投资先机。【第一财经智享会员专属】晨会博弈【今日早盘】...
万亿稳定币市场开启:从概念炒作... 文|恒心来源|博望财经数字货币概念港股又迎来利好消息。据《金融界》等多家媒体报道,中国香港特别行政区...
北交所打新持续火热,新三板公司... 新京报贝壳财经讯(记者黄鑫宇)中签率0.17%、0.04%、0.03%……今年以来北交所新股申购行情...
增长53%!1至4月雄安民营企... 河北日报客户端讯(康晓博、王敏)从雄安海关获悉,今年1至4月,雄安新区进出口总值14.7亿元,同比增...
深圳华大智造科技股份有限公司关... 本公司董事会及全体董事保证本公告内容不存在任何虚假记载、误导性陈述或者重大遗漏,并对其内容的真实性、...
金价狂飙,金表也不好卖了 泡泡玛特、老铺黄金、蜜雪冰城股价屡创新高,被港股市场称为“新消费三姐妹”。与它们有着异曲同工之妙的金...
汉口银行拟吸收业绩下滑资产,谋... 文 | 刘振涛谋求IPO的汉口银行,拟吸收合并村镇银行,谋划异地扩张。近日,汉口银行发布公告,将在6...
大连银行迎新帅 能否走出“消失... “ 过去十年,大连银行业绩可谓是原地踏步,2018年到2023年,净利润更是六连降。去年,大连银行结...
雷军凶猛,兵临董明珠城下 雷军... 家电大战,全面开打。文 | 华商韬略 东木褚小米的大家电业务爆发了,营收首次突破1000亿大关。去年...
财报里的十大跨境巨头:出海增长... 作者 | 唐飞编辑 | 谭格2024年的跨境电商江湖,正经历着前所未有的冰火两重天。一边是安克创新靠...
A股、港股云计算概念双双走强,... 6月5日,三大指数集体高开,盘中创业板指走高,港股方面,恒生科技指数涨幅走阔。A股、港股云计算概念双...
财报解读|蔚来一季度营收增长2... 21世纪经济报道记者 郑植文 上海报道6月3日,蔚来汽车发布了2025年第一季度财报。数据显示,20...