【MyBatis源码】MapperRegistry详解
🎮 作者主页:点击
🎁 完整专栏和代码:点击
🏡 博客主页:点击
文章目录
- MapperRegistry 的作用
- 核心字段解析
- 整体工作流程
- addMapper方法
- MapperAnnotationBuilder#parse流程详解
- MapperAnnotationBuilder#parseStatement
- 处理主键生成策略(KeyGenerator)
- 处理缓存、超时、ResultMap 等配置
- 处理 ResultMap
- 注册 MappedStatement
MapperRegistry 的作用
MapperRegistry 是 MyBatis 核心组件之一,主要负责管理 Mapper 接口的注册和获取。当通过 sqlSession.getMapper(XXXMapper.class) 获取 Mapper 实例时,会通过 MapperRegistry 返回相应的 Mapper 代理对象。
核心字段解析
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
• Configuration:MyBatis 的全局配置对象,管理 MyBatis 的各种配置信息。
• knownMappers:用于存储已经注册的 Mapper 接口及其对应的 MapperProxyFactory。
整体工作流程
- Mapper 注册:
◦ 通过 addMapper 方法注册 Mapper 接口,将其与 MapperProxyFactory 绑定,并解析注解或 XML 配置文件。 - Mapper 获取:
◦ 通过 getMapper 方法获取 Mapper 实例,实际返回的是一个动态代理对象 (MapperProxy)。 - 动态代理执行:
◦ 当调用 Mapper 方法时,代理对象 (MapperProxy) 会拦截方法调用,并根据方法上的注解或 XML 配置执行对应的 SQL 语句。
addMapper方法
MapperRegistry#addMapper 是 MyBatis 框架中的一个核心方法,用于将 Mapper 接口注册到 MyBatis 环境中。这一过程确保 MyBatis 能够正确识别和管理 Mapper 接口,以便在实际使用时生成相应的代理对象。
public <T> void addMapper(Class<T> type) {// 判断是否是接口,如果不是接口,直接返回if (type.isInterface()) {if (hasMapper(type)) {// 防止重复注册,如果已经注册过,则抛出异常throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 添加到 knownMappers(一个 Map<Class<?>, MapperProxyFactory<?>>)中knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.// 检查该接口是否有与之关联的 XML 映射文件MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
MapperAnnotationBuilder#parse流程详解
MapperRegistry#addMapper方法流程最后是将解析流程交给了MapperAnnotationBuilder#parse,下面针对这个方法进行详细分析。
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse 是 MyBatis 中用来解析 @Mapper 接口中注解的方法,它的作用是扫描并处理 @Select, @Insert, @Update, @Delete 等 SQL 操作注解,生成对应的 MappedStatement 并将其注册到 Configuration 中。
public void parse() {String resource = type.toString();// 检查资源是否已加载if (!configuration.isResourceLoaded(resource)) {// 解析加载mapper关联的XML文件信息,如果是注解形式则为空loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());// 解析缓存和缓存引用parseCache();parseCacheRef();// 获取当前 Mapper 接口中的所有方法for (Method method : type.getMethods()) {// 调用 canHaveStatement(method) 方法检查该方法是否适合生成 MappedStatementif (!canHaveStatement(method)) {continue;}// 处理Select和SelectProvider注解方法if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()&& method.getAnnotation(ResultMap.class) == null) {parseResultMap(method);}try {// 解析 Mapper 方法上的注解并生成 MappedStatementparseStatement(method);} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}
• 通过 configuration.isResourceLoaded(resource) 检查当前 Mapper 是否已经被加载过,如果加载过则直接返回,避免重复加载。如果没有加载过,则将其标记为已加载。
• loadXmlResource用于加载 XML 资源(即 Mapper.xml 文件),并确保其与 @Mapper 注解的接口绑定在一起。如果 Mapper.xml 存在,则将其加载到 Configuration 中,并将其中的 select, insert, update, delete 等语句转换为 MappedStatement。
• assistant.setCurrentNamespace(type.getName()) 的作用是将当前 Mapper 接口的全限定类名(如 com.example.mapper.UserMapper)设置为命名空间。MyBatis 中的命名空间是用于隔离不同 Mapper 的关键机制,确保每个 MappedStatement 的 id 是唯一的,避免冲突。
• canHaveStatement(Method method) 方法用于判断当前 Mapper 接口中的某个方法是否可以解析为 MyBatis 的 MappedStatement。这意味着只有符合条件的方法,MyBatis 才会尝试为其生成对应的 SQL 映射语句(MappedStatement)。method.isBridge()排除桥接方法。method.isDefault() 用于检查该方法是否为 Java 8 引入的接口默认方法。
MapperAnnotationBuilder#parseStatement
这段代码是 org.apache.ibatis.builder.annotation.MapperAnnotationBuilder 类的一部分,其作用是从 Mapper接口的方法(通常标注有 @Select、@Insert、@Update 或 @Delete 注解的方法)中提取信息,并构建出 MappedStatement 对象,用于 SQL 的执行。
以下是方法的总体思路:
- 解析方法参数类型。
- 获取方法所使用的语言驱动(LanguageDriver)。
- 从注解中构建 SqlSource 和 SqlCommandType。
- 解析主键生成策略。
- 解析缓存、超时、结果集等配置。
- 生成并注册 MappedStatement。
final Class<?> parameterTypeClass = getParameterType(method);
final LanguageDriver languageDriver = getLanguageDriver(method);
• getParameterType(method):获取方法的参数类型。如果方法有多个参数,则 MyBatis 会将其封装为 Map 类型。
• getLanguageDriver(method):确定使用哪种语言驱动,默认是 XMLLanguageDriver。语言驱动用于解析 SQL 语句,可以是注解中的 SQL,也可以是 XML 配置中的 SQL。
getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
private SqlSource buildSqlSource(Annotation annotation, Class<?> parameterType, LanguageDriver languageDriver,Method method) {if (annotation instanceof Select) {return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Update) {return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Insert) {return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Delete) {return buildSqlSourceFromStrings(((Delete) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof SelectKey) {return buildSqlSourceFromStrings(((SelectKey) annotation).statement(), parameterType, languageDriver);}return new ProviderSqlSource(assistant.getConfiguration(), annotation, type, method);}
• getAnnotationWrapper():获取方法上的 SQL 注解(如 @Select、@Insert 等)。
• buildSqlSource():根据注解内容生成 SqlSource 对象,SqlSource 用于封装 SQL 语句,并处理占位符参数(如 #{})。
• getSqlCommandType():确定 SQL 命令类型,如 SELECT、INSERT、UPDATE、DELETE。
final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options)x.getAnnotation()).orElse(null);
它的作用是从 Mapper 方法中获取 @Options 注解,并将其解析为 Options 对象,以便在后续配置 MappedStatement时使用。MyBatis 提供了 @Options 注解,用于配置 Mapper 方法的一些额外选项。常见的配置选项包括:useCache:是否启用二级缓存。timeout:SQL 执行超时时间。
处理主键生成策略(KeyGenerator)
// 处理主键生成策略(KeyGenerator)final KeyGenerator keyGenerator;String keyProperty = null;String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// 如果方法上有 @SelectKey 注解,使用其生成主键SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey)x.getAnnotation()).orElse(null);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {// 默认使用 JDBC3 的主键生成方式keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}
• 如果 SqlCommandType 是 INSERT 或 UPDATE,则可能需要生成主键。
• 优先使用 @SelectKey 注解来生成主键;如果没有,则根据配置选择是否自动生成主键。
• keyProperty 和 keyColumn 用于指定主键映射。
处理缓存、超时、ResultMap 等配置
// 处理缓存、超时、ResultMap 等配置Integer fetchSize = null;Integer timeout = null;// statementType:语句类型,默认为 PREPARED,还可以是 STATEMENT 或 CALLABLE。StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = configuration.getDefaultResultSetType();boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;if (options != null) {// 从方法上的 @Options 注解中提取配置,如 flushCache、useCache、fetchSize 和 timeout。if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();if (options.resultSetType() != ResultSetType.DEFAULT) {resultSetType = options.resultSetType();}}
处理 ResultMap
// 处理 ResultMapString resultMapId = null;if (isSelect) {// 如果方法是 SELECT 查询,则可能需要 ResultMap 来映射结果集到 Java 对象。ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {// 优先使用 @ResultMap 注解;如果没有,则自动生成 ResultMap。resultMapId = String.join(",", resultMapAnnotation.value());} else {resultMapId = generateResultMapName(method);}}
注册 MappedStatement
// 注册 MappedStatementassistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,statementAnnotation.getDatabaseId(),languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);
• 调用 assistant.addMappedStatement() 将解析好的信息封装成 MappedStatement 并注册到 MyBatis 的 Configuration 中。
• MappedStatement 包含了执行 SQL 所需的所有信息,包括 SQL 语句、参数类型、结果集映射、缓存策略等。
相关文章:
【MyBatis源码】MapperRegistry详解
🎮 作者主页:点击 🎁 完整专栏和代码:点击 🏡 博客主页:点击 文章目录 MapperRegistry 的作用核心字段解析整体工作流程addMapper方法MapperAnnotationBuilder#parse流程详解MapperAnnotationBuilder#parse…...

Java项目实战II基于Spring Boot的工作流程管理系统设计与实现(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在当今快节奏的商业环境中,…...
flink StreamGraph 构造flink任务
文章目录 背景主要步骤代码 背景 通常使用flink 提供的高级算子来编写flink 任务,对底层不是很了解,尤其是如何生成作业图的细节 下面通过构造一个有向无环图,来实际看一下 主要步骤 1.增加source 2.增加operator 3. 增加一条边࿰…...

【51单片机】LCD1602液晶显示屏
学习使用的开发板:STC89C52RC/LE52RC 编程软件:Keil5 烧录软件:stc-isp 开发板实图: 文章目录 LCD1602存储结构时序结构 编码 —— 显示字符、数字 LCD1602 LCD1602(Liquid Crystal Display)液晶显示屏是…...
理解 HTML5 Canvas 中逻辑像素与物理像素的关系
理解 HTML5 Canvas 中逻辑像素与物理像素的关系 在使用 HTML5 Canvas 时,开发者经常会遇到一个困惑:为什么鼠标的 offsetX 和 offsetY 和我绘制的图形坐标对不上?这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这…...

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析
揭秘C语言输入输出内幕:printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾: VS 2022 社区版C语言的安装教程,不要再卡在下载0B/s啦C语言入门:解锁基础概念,动手实现首个C程序C语言概念之旅:解锁关…...

数据分析-系统认识数据分析
目录 数据分析的全貌 观测 实验 应用 数据分析的全貌 观测 实验 应用...
蓝桥杯介绍
赛事背景与历程 自2009年举办以来,蓝桥杯已经连续举行了多届,成为国内领先的信息技术赛事。2022年,蓝桥杯被教育部确定为2022—2025学年面向中小学生的全国性竞赛活动,并入选国家级A类学科竞赛。 参赛对象与组别 蓝桥杯的参赛对…...

鸿蒙加载网络图片并转换成PixelMap
鸿蒙加载网络图片并转换成PixelMap 参考文档 基于API12. 有一些图片功能需要使用 PixelMap 类型的参数,但是使用Image组件之类的时候无法获取到 PixelMap 类型数据。 因此只能是把图片下载下来然后加在并转换一下。 实现方式 一下封装了一个函数。使用的 rcp 模…...

hive搭建
1.准备环境 三台节点主机已安装hadoopmysql数据库 2.环境 2.1修改三台节点上hadoop的core-site.xml <!-- 配置 HDFS 允许代理任何主机和组 --> <property><name>hadoop.proxyuser.hadoop.hosts</name><value>*</value> </property&…...

51c扩散模型~合集1
我自己的原文哦~ https://blog.51cto.com/whaosoft/11541675 #Diffusion Forcing 无限生成视频,还能规划决策,扩散强制整合下一token预测与全序列扩散 当前,采用下一 token 预测范式的自回归大型语言模型已经风靡全球,同时互联…...

从零开始深度学习:全连接层、损失函数与梯度下降的详尽指南
引言 在深度学习的领域,全连接层、损失函数与梯度下降是三块重要的基石。如果你正在踏上深度学习的旅程,理解它们是迈向成功的第一步。这篇文章将从概念到代码、从基础到进阶,详细剖析这三个主题,帮助你从小白成长为能够解决实际…...
Liebherr利勃海尔 EDI 需求分析
Liebherr 使用 EDI 技术来提高业务流程的效率、降低错误率、加快数据交换速度,并优化与供应商、客户和其他合作伙伴之间的业务沟通。通过 EDI,Liebherr 实现了与全球交易伙伴的自动化数据交换,提升了供应链管理和订单处理的透明度。 Liebher…...
java小练习
小练1.用while语句计算11/2!1/3!1/4!...1/20!的和 public class test_11_17_2 {public static void main(String[] args) {double sum 0;double item 1;int n 20;int i 1;while(i<n){sum item;i i1;item item*(1.0/i);}System.out.println(sum);} } 小练2.计算88888…...
go语言中的占位符有哪些
在Go语言中,占位符主要用于格式化字符串输出,特别是在使用fmt包中的Printf系列函数时。以下是Go语言中常用的占位符: %v:代表值的默认格式,对于字符串是直接输出,对于整型是十进制形式。%v:扩展…...
基于Windows安装opus python库
项目中需要用到一些opus格式的编解码功能,找到网上有opus的开源库。网址:Opus Codec 想着人生苦短,没想到遇上了错误!在这里记录一下过程 过程 安装python库 pip3 install opuslib验证 >>> import opuslib Tracebac…...

【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
《设计模式之行为型模式》系列,共包含以下文章: 行为型模式(一):模板方法模式、观察者模式行为型模式(二):策略模式、命令模式行为型模式(三):责…...

使用nossl模式连接MySQL数据库详解
使用nossl模式连接MySQL数据库详解 摘要一、引言二、nossl模式概述2.1 SSL与nossl模式的区别2.2 选择nossl模式的场景三、在nossl模式下连接MySQL数据库3.1 准备工作3.2 C++代码示例3.3 代码详解3.3.1 初始化MySQL连接对象3.3.2 连接到MySQL数据库3.3.3 执行查询操作3.3.4 处理…...

【MySQL】ubantu 系统 MySQL的安装与免密码登录的配置
🍑个人主页:Jupiter. 🚀 所属专栏:MySQL初阶探索:构建数据库基础 欢迎大家点赞收藏评论😊 目录 📚mysql的安装📕MySQL的登录🌏MySQL配置免密码登录 📚mysql的…...

高级 SQL 技巧讲解
大家好,我是程序员小羊! 前言: SQL(结构化查询语言)是管理和操作数据库的核心工具。从基本的查询语句到复杂的数据处理,掌握高级 SQL 技巧不仅能显著提高数据分析的效率,还能解决业务中的复…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...