Spring 整合 Mybatis 原理

article/2025/9/1 12:48:05

目录

  • Mybatis的基本工作原理
  • 分析需要解决的问题
  • Spring中Bean的产生过程
  • 解决问题
  • 解决方案
    • FactoryBean
  • Import
  • 总结
  • 优化

Mybatis的基本工作原理

在 Mybatis 中,我们可以使用一个接口去定义要执行 sql,简化代码如下:定义一个接口,@Select 表示要执行查询 sql 语句。

public interface UserMapper {@Select("select * from user where id = #{id}")User selectById(Integer id);
}

以下为执行 sql 代码:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer id = 1;
User user = mapper.selectById(id);

Mybatis的目的是使得程序员能够以调用方法的方式执行某个指定的 sql,将执行 sql 的底层逻辑进行了封装。

当调用 SqlSession 的 getMapper 方法时,会对传入的接口生成一个代理对象,而程序要真正用到的就是这个代理对象,在调用代理对象的方法时,Mybatis 会取出该方法所对应的 sql 语句,然后利用JDBC 去执行 sql 语句,最终得到结果。

分析需要解决的问题

Spring 整和 Mybatis 时,我们重点要关注的就是这个代理对象。因为整合的目的就是:把某个 Mapper 的代理对象作为一个 bean 放入 Spring 容器中,使得能够像使用一个普通 bean 一样去使用这个代理对象,比如能被 @Autowire 自动注入。

当 Spring 和 Mybatis 整合之后,我们就可以使用如下的代码来使用Mybatis中的代理对象了:

@Component
public class UserService {@Autowiredprivate UserMapper userMapper;public User getUserById(Integer id) {return userMapper.selectById(id);}
}

UserService 中的 userMapper 属性就会被自动注入为 Mybatis 中的代理对象。如果你基于一个已经完成整合的项目去调试即可发现,userMapper 的类型为:org.apache.ibatis.binding.MapperProxy@41a0aa7d。证明确实是 Mybatis 中的代理对象。

如何能够把 Mybatis 的代理对象作为一个 bean 放入 Spring 容器中?要解决这个,我们需要对 Spring 的 bean 生成过程有一个了解。

Spring中Bean的产生过程

Spring 启动过程中,大致会经过如下步骤去生成 bean:

  1. 扫描指定的包路径下的 class 文件
  2. 根据 class 信息生成对应的 BeanDefinition
  3. 在此处,程序员可以利用某些机制去修改 BeanDefinition
  4. 根据 BeanDefinition 生成 bean 实例
  5. 把生成的 bean 实例放入 Spring 容器中

假设有一个 A 类,假设有如下代码:

一个 A 类:

@Component
public class A {
}

一个 B 类,不存在 @Component 注解

public class B {
}

执行如下代码:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean("a"));// 输出结果为
com.luban.util.A@6acdbdf5

A 类对应的 bean 对象类型仍然为 A 类。但是这个结论是不确定的,我们可以利用 BeanFactory 后置处理器来修改 BeanDefinition ,我们添加一个 BeanFactory 后置处理器:

@Component
public class LubanBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("a");beanDefinition.setBeanClassName(B.class.getName());}
}

这样就会导致,原本的 A 类对应的 BeanDefiniton 被修改了,被修改成了 B 类,那么后续正常生成的 bean 对象的类型就是 B 类。此时,调用如下代码会报错:

context.getBean(A.class);

但是调用如下代码不会报错,尽管 B 类上没有 @Component 注解:

context.getBean(B.class);

并且,下面代码返回的结果是:com.luban.util.B@4b1c1ea0

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean("a"));

之所以讲这个问题,是想说明一个问题:在 Spring 中,bean 对象跟 class 没有直接关系,跟 BeanDefinition 才有直接关系。

那么如何能够把 Mybatis 的代理对象作为一个 bean 放入 Spring 容器中?

在 Spring 中,如果你想生成一个 bean,那么得先生成一个 BeanDefinition ,就像你想 new 一个对象实例,得先有一个 class 。

解决问题

我们现在想自己生成一个 bean ,那么得先生成一个 BeanDefinition ,只要有了 BeanDefinition ,通过在 BeanDefinition 中设置bean对象的类型,然后把 BeanDefinition 添加给 Spring ,Spring 就会根据 BeanDefinition 自动帮我们生成一个类型对应的 bean 对象。

所以,现在我们要解决两个问题:

1. Mybatis 的代理对象的类型是什么?因为我们要设置给 BeanDefinition
2. 我们怎么把 BeanDefinition 添加给 Spring 容器?

上文中我们使用的 BeanFactory 后置处理器,他只能修改 BeanDefinition ,并不能新增一个 BeanDefinition 。我们应该使用 Import 技术来添加一个 BeanDefinition 。后文再详细介绍如果使用Import 技术来添加一个 BeanDefinition ,可以先看一下伪代码实现思路。

假设:我们有一个 UserMapper 接口,他的代理对象的类型为 UserMapperProxy。那么我们的思路就是这样的,伪代码如下:

BeanDefinitoin bd = new BeanDefinitoin();
bd.setBeanClassName(UserMapperProxy.class.getName());
SpringContainer.addBd(bd);

但是,这里有一个严重的问题,就是上文中的 UserMapperProxy 是我们假设的,他表示一个代理类的类型,然而 Mybatis 中的代理对象是利用的 JDK 的动态代理技术实现的,也就是代理对象的代理类是动态生成的,我们根本无法确定代理对象的代理类到底是什么。

所以回到我们的问题:Mybatis的代理对象的类型是什么?

本来可以有两个答案:

  1. 代理对象对应的代理类
  2. 代理对象对应的接口

那么答案 1 就相当于没有了,因为是代理类是动态生成的,那么我们来看答案 2 代理对象对应的接口。

BeanDefinition bd = new BeanDefinitoin();
// 注意这里,设置的是UserMapper
bd.setBeanClassName(UserMapper.class.getName());
SpringContainer.addBd(bd);

但是,实际上给 BeanDefinition 对应的类型设置为一个接口是行不通的,因为 Spring 没有办法根据这个 BeanDefinition 去 new 出对应类型的实例,接口是没法直接 new 出实例的。

那么现在问题来了,我要解决的问题:Mybatis的代理对象的类型是什么?

两个答案都被我们否定了,所以这个问题是无解的,所以我们不能再沿着这个思路去思考了,只能回到最开始的问题:如何能够把 Mybatis 的代理对象作为一个 bean 放入 Spring 容器中?

总结上面的推理:我们想通过设置 BeanDefinition 的 class 类型,然后由 Spring 自动的帮助我们去生成对应的 bean,但是这条路是行不通的。

解决方案

那么我们还有没有其他办法,可以去生成 bean 呢?并且生成 bean 的逻辑不能由 Spring 来帮我们做了,得由我们自己来做。

FactoryBean

有,那就是 Spring 中的 FactoryBean。我们可以利用 FactoryBean 去自定义我们要生成的 bean 对象,比如:
在这里插入图片描述

我们定义了一个 LubanFactoryBean,它实现了 FactoryBean ,getObject 方法就是用来自定义生成 bean 对象逻辑的。

执行如下代码:

public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);System.out.println("lubanFactoryBean: " + context.getBean("lubanFactoryBean"));System.out.println("&lubanFactoryBean: " + context.getBean("&lubanFactoryBean"));System.out.println("lubanFactoryBean-class: " + context.getBean("lubanFactoryBean").getClass());}
}// 输出
lubanFactoryBean: com.luban.util.LubanFactoryBean$1@4d41cee
&lubanFactoryBean: com.luban.util.LubanFactoryBean@3712b94
lubanFactoryBean-class: class com.sun.proxy.$Proxy20

从结果我们可以看到,从 Spring 容器中拿名字为"lubanFactoryBean"的 bean 对象,就是我们所自定义的 jdk 动态代理所生成的代理对象。

所以,我们可以通过 FactoryBean 来向 Spring 容器中添加一个自定义的 bean 对象。上文中所定义的 LubanFactoryBean 对应的就是 UserMapper ,表示我们定义了
一个 LubanFactoryBean,相当于把 UserMapper 对应的代理对象作为一个 bean 放入到了容器中。

但是作为程序员,我们不可能每定义了一个 Mapper ,还得去定义一个 LubanFactoryBean ,这是很麻烦的事情,我们改造一下 LubanFactoryBean ,让他变得更通用,比如:
在这里插入图片描述
改造 LubanFactoryBean 之后,LubanFactoryBean 变得灵活了,可以在构造 LubanFactoryBean 时,通过构造传入不同的 Mapper 接口。

实际上 LubanFactoryBean 也是一个 Bean,我们也可以通过生成一个 BeanDefinition 来生成一个 LubanFactoryBean ,并给构造方法的参数设置不同的值,比如伪代码如下:

BeanDefinition bd = new BeanDefinitoin();
// 注意一:设置的是LubanFactoryBean
bd.setBeanClassName(LubanFactoryBean.class.getName());
// 注意二:表示当前BeanDefinition在生成bean对象时,会通过调用LubanFactoryBean的构造方法来生成,并传入UserMapper
bd.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class.getName())
SpringContainer.addBd(bd);

特别说一下注意二,表示当前 BeanDefinition 在生成 bean 对象时,会通过调用 LubanFactoryBean 的构造方法来生成,并传入 UserMapper 的 Class 对象。那么在生成 LubanFactoryBean 时就会生成一个 UserMapper 接口对应的代理对象作为 bean 了。

到此为止,其实就完成了我们要解决的问题:把 Mybatis 中的代理对象作为一个 bean 放入 Spring 容器中。只是我们这里是用简单的 JDK 代理对象模拟的 Mybatis 中的代理对象,如果有时间,我们完全可以调用 Mybatis 中提供的方法区生成一个代理对象。这里就不花时间去介绍了。

Import

到这里,我们还有一个事情没有做,就是怎么真正的定义一个 BeanDefinition ,并把它添加到 Spring 中,上文说到我们要利用 Import 技术,比如可以这么实现

定义如下类:
在这里插入图片描述
并且在 AppConfig 上添加 @Import 注解:

@Import(LubanImportBeanDefinitionRegistrar.class)
public class AppConfig {

这样在启动 Spring 时就会新增一个 BeanDefinition,该 BeanDefinition 会生成一个 LubanFactoryBean 对象,并且在生成 LubanFactoryBean 对象时会传入 UserMapper.class 对象,通过 LubanFactoryBean 内部的逻辑,相当于会自动生产一个 UserMapper 接口的代理对象作为一个 bean。

总结

总结一下,通过我们的分析,我们要整合Spring和Mybatis,需要我们做的事情如下:

  1. 定义一个 LubanFactoryBean
  2. 定义一个 LubanImportBeanDefinitionRegistrar
  3. 在 AppConfig 上添加一个注解 @Import(LubanImportBeanDefinitionRegistrar.class)

优化

这样就可以基本完成整合的需求了,当然还有两个点是可以优化的
第一,单独再定义一个 @LubanScan 的注解,如下:

@Retention(RetentionPolicy.RUNTIME)
@Import(LubanImportBeanDefinitionRegistrar.class)
public @

这样在 AppConfig 上直接使用 @LubanScan 即可

第二,在 LubanImportBeanDefinitionRegistrar 中,我们可以去扫描 Mapper ,在LubanImportBeanDefinitionRegistrar 我们可以通过 AnnotationMetadata 获取到对应的 @LubanScan 注解,所以我们可以在 @LubanScan 上设置一个 value ,用来指定待扫描的包路径。然后在 LubanImportBeanDefinitionRegistrar 中获取所设置的包路径,然后扫描该路径下的所有 Mapper,生成 BeanDefinition,放入 Spring 容器中。

所以,到此为止,Spring 整合 Mybatis 的核心原理就结束了,再次总结一下:

  1. 定义一个 LubanFactoryBean,用来将 Mybatis 的代理对象生成一个 bean 对象
  2. 定义一个 LubanImportBeanDefinitionRegistrar,用来生成不同 Mapper 对象的 LubanFactoryBean
  3. 定义一个 @LubanScan,用来在启动 Spring 时执行 LubanImportBeanDefinitionRegistrar 的逻辑,并指定包路径

以上这个三个要素分别对象 org.mybatis.spring 中的:
MapperFactoryBean
MapperScannerRegistrar
@MapperScan


http://chatgpt.dhexx.cn/article/Nj9VJFrq.shtml

相关文章

Springboot整合mybatis原理

文章目录 加载过程1、读取META-INF/spring.factories配置文件里需要自动装载的类2、解析MybatisAutoConfiguration类里的注解信息,将需要管理的Bean注册到Spring容器2.1 注册SqlSessionFactory,并根据mapper配置文件解析出dao与具体jdbc操作、resultMap与…

【mybatis原理】

mybatis mybatis原理mybatis框架分层架构核心接口和对象mapper接口与xml的映射mybatis执行过程mybatis执行时序图一级缓存和二级缓存一级缓存二级缓存 mybatis核心流程1、初始化阶段2、代理阶段3、数据读写阶段 mybatis如何获取数据源mybatis如何获取执行SQLMyBatis 如何执行 s…

Mybatis原理

文章目录 - 什么是Mybatis?- Mybaits的优点:- MyBatis框架的缺点:- MyBatis与Hibernate有哪些不同?- 架构MyBatis缓存一级缓存一级缓存和sqlsession之间的关系一级缓存的生命周期有多长?SqlSession 一级缓存的工作流程…

深入详解Mybatis的架构原理与6大核心流程

MyBatis 是 Java 生态中非常著名的一款 ORM 框架,目前在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。 如果你想要进入一线大厂,能够熟练使用 MyBatis 开发已经是一项非常基本的技能,同时大厂也更希望自己的开发人员…

总结Mybatis的原理

一、什么是Mybatis Mybatis是一个半ORM(对象关系映射)框架,底层封装了JDBC,是程序员在开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。MyBatis 可以使用简单的 XM…

MyBatis基本工作原理介绍

1、MyBatis基本工作原理介绍 计算机的基本工作就是存储和计算,而MyBatis是存储领域的利器。MyBatis的基本工作原理就是:先封装SQL,接着调用JDBC操作数据库,最后把数据库返回的表结果封装成Java类。 2、MyBatis的核心流程介绍 m…

Mybatis 工作原理详解

目录 Mybatis持久层框架 结果集进行ORM映射 步骤解析 1、获取结果集及结果映射入口 2、开始ORM映射接口 3、数据库结果集解析,完成ORM映射 4、保存并获取ORM映射后的结果集 参数传递方式 顺序传参法 Param注解传参法 Map传参法 Java Bean传参法 mybat…

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路,并且讨论MyBatis的几个核心部件,然后结合一个select查询实例,深入代码,来探究MyBatis的实…

【图片压缩】三个方法压缩图片体积

图片体积过大,会占用电脑过多的容量,也有可能无法发送给其他人。我们可以压缩图片体积来解决问题。分享三个方法压缩图片体积: 方法一:改变图片格式 首先是改变图片的格式,如果你不介意压缩之后的图片画质损失&#…

Windows不同压缩软件、压缩算法、压缩率详细对比测试与选择

上次写了图片压缩,这倒让我想起几年前看过的一个很有意思的东西 那就是这张鸭子图: 不过微信会压缩图片,你可以打开这个链接:http://2.im.guokr.com/F70Kn-4wz7aF5Yejf9W3g6kO4exDBqVEb0TumQmxy5MiAQAAEAEAAEpQ.jpg 来获取原图 …

为什么压缩图片和压缩

为什么要压缩图片? 表示图像需要大量的数据,但图像数据是高度相关的,或者说存在冗余(Redundancy)信息,去掉这些冗余信息后可以有效压缩图像,同时又不会损害图像的有效信息。 视网膜上有两种感光细胞,能够…

2款免费的图片压缩工具

今天写这篇文章的目的,主要是为大家介绍2款免费的图片压缩工具,在工作和学习中这也是一项必备的技能。比如当你遇到比较大的图片需要发送的时候,或者对图片的大小有强制要求的时候,这些小工具就派上了用场。 网上也有一些付费的压…

如何批量压缩图片体积大小kb?

工作中,我们在使用图片素材时,图片kb体积过大怎么办? 通常我们会直接在电脑上将图片调整成我们想要的尺寸大小,可以利用的工具有photoshop或者截图的方法,这对于有些小伙伴来说不是很难的事情,今天我就不做…

图片批量压缩方法及步骤

图片批量压缩方法及步骤!平常我们会将手机拍摄的照片传输到电脑里保存,时间久了后电脑中会有大量的图片,这些图片大都是1M-2M的体积大小,这些图片会占用大量的电脑磁盘空间,可能会导致电脑变得很卡等现象。但是又不忍心…

银河麒麟批量压缩图片的方法

适用系统:银河麒麟V10(SP1),CPU:Kirin990,架构:aarch64。 软件商店下载“简单图像压缩转换软件”。桌面左下角点开菜单搜索“Simple Image Reducer”,右键添加到桌面快捷方式。打开Simple Image Reducer。…

python压缩图片和视频

引言 在真实项目中,往往要进行图片的传输和视频的传输,但因为用户上传的图片和视频所占用的大小问题而导致占用服务器的空间多,并且用户访问这些图片的时候因为图片或视频太大而长时间加载,所以要对用户上传的视频和图片进行压缩…

7款最好用的图片无损,视频无损压缩软件

第一:QVE视频压缩软件 下载地址:http://www.qvevideo.com/compress 非常实用的视频,图片两用压缩软件,能够将各种视频压缩成mp4,flv等格式, 缩减视频体积,释放磁盘空间,节省网络带宽,压缩后能够保持视频高清晰度,压缩率高达90%以上。 1,首先打开软件,切换到【图片压缩】…

苹果手机解压缩软件_照片压缩软件哪款好用?推荐5款好用的图片压缩软件

在我们办公的时候,总会遇到各种各样的问题,就比如PDF与文档之间的转换,图片的压缩,文档加密的方法等等,这些都是上班族经常需要处理的问题,特别是图片压缩,很多人都想知道照片压缩软件哪款好&am…

分享一个好用的图片压缩软件

为了性能优化需要,一般需要优化网站上的图片,减少大小。但问题来了,很多压缩软件是有损压缩,压缩后图片质量惨不忍睹。 下面我分享一下刚刚了解到的图片压缩软件,名字叫智图。 官网地址是: http://zhitu.is…

无损对图片进行压缩软件Caesium使用方法及下载

无损对图片进行压缩,除了用PS外,还有一款小白也可以用的软件Caesium。 使用方法如下: 1、打开文件夹,双击Caesium.exe 2、弹出的界面如下 3、点击左上角红色方框打开文件5837014_0_0.jpg,压缩选项的品质任意修改&…