spring整合mybatis的底层原理

spring整合mybatis的底层原理

原理:

  1. FactoryBean的自定义对象
  2. jdk动态代理Mapper接口对象

一、手写一个spring集成mybatis

目录结构:
在这里插入图片描述

1.1 入口类
public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(AppConfig.class);context.refresh();UserService userService = (UserService)context.getBean("userService");userService.test();}
}
1.2 配置类
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
@ComponentScan("com.athome.tulin.springmybatis")
public class AppConfig {@Beanpublic SqlSessionFactory sqlSessionFactory() throws IOException {System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;}
}
1.3 业务类
@Component
public class UserService {public UserService() {System.out.println("3.…………创建UserService…………");}//如何把mybatis生成的UserMapper的代理对象赋值给UserMapper@Autowiredprivate UserMapper userMapper;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate MemberMapper memberMapper;public void test(){System.out.println("7.……UserService…test…");System.out.println(userMapper.selectById());System.out.println(orderMapper.selectById());System.out.println(memberMapper.selectById());}
}
1.4 创建3个Mapper接口
public interface MemberMapper {@Select("select 'member' ")String selectById();
}
public interface OrderMapper {@Select("select 'order' ")String selectById();
}
public interface UserMapper {@Select("select 'user' ")String selectById();
}
1.5 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(CondorHeroImportBeanDefinitionRegistrar.class)
public @interface CondorHeroMapperScan {String value();
}
1.6 自定义fanctoryBean
public class CondorHeroFactoryBean implements FactoryBean {private Class mapperInterface;private SqlSession sqlSession;public CondorHeroFactoryBean(Class mapperInterface) {this.mapperInterface = mapperInterface;}/*** 从容器查找SqlSessionFactory 并获取sqlSession 赋值于sqlSession* 扫描的时候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);* 那他就会自动找set方法* @param sqlSessionFactory*/public void setSqlSession(SqlSessionFactory sqlSessionFactory) {System.out.println("5.……setSqlSession……");sqlSessionFactory.getConfiguration().addMapper(mapperInterface);this.sqlSession = sqlSessionFactory.openSession();}@Overridepublic Object getObject() throws Exception {//动态代理获取UserMapper接口对象System.out.println("6.……getObject……");return sqlSession.getMapper(mapperInterface);}@Overridepublic Class<?> getObjectType() {return mapperInterface;}
}
1.7 自定义Bean注册类
public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {System.out.println("1.……registerBeanDefinitions……");//1.获取注解上的指定路径Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(CondorHeroMapperScan.class.getName());String path = (String)annotationAttributes.get("value");//2.扫描CondorHeroBeanDefinitionScanner scanner = new CondorHeroBeanDefinitionScanner(registry);scanner.addIncludeFilter(new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});scanner.scan(path);}
}
1.8 自定义扫描类
public class CondorHeroBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public CondorHeroBeanDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)  {//只关心接口(判断是否是接口)return beanDefinition.getMetadata().isInterface();}/*** 扫描路径并得到beanDefinition* @param basePackages* @return*/@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {System.out.println("2.……doScan……");Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) {GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition();//设置值beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());//设置名称beanDefinition.setBeanClassName(CondorHeroFactoryBean.class.getName());//将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}return beanDefinitionHolders;}
}

运行结果是:
1.……registerBeanDefinitions……
2.……doScan……
3.…………创建UserService…………
4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
7.……UserService…test… user order member

二、原理解析

2.1 通过@MapperScan导入了MapperScannerRegistrar类

在这里插入图片描述

2.2 MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法

在这里插入图片描述

2.3 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition

在这里插入图片描述

2.4 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法

在这里插入图片描述

2.5 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描(scanner.scan)
2.6 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition

在这里插入图片描述

2.7 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType(在类ClassPathMapperScanner的方法processBeanDefinitions中)
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {GenericBeanDefinition definition;for (BeanDefinitionHolder holder : beanDefinitions) {definition = (GenericBeanDefinition) holder.getBeanDefinition();String beanClassName = definition.getBeanClassName();LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName+ "' mapperInterface");// the mapper interface is the original class of the bean// but, the actual class of the bean is MapperFactoryBean//设置值definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59//设置名称definition.setBeanClass(this.mapperFactoryBeanClass);definition.getPropertyValues().add("addToConfig", this.addToConfig);boolean explicitFactoryUsed = false;if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {definition.getPropertyValues().add("sqlSessionFactory",new RuntimeBeanReference(this.sqlSessionFactoryBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionFactory != null) {definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);explicitFactoryUsed = true;}if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {if (explicitFactoryUsed) {LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate",new RuntimeBeanReference(this.sqlSessionTemplateBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionTemplate != null) {if (explicitFactoryUsed) {LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);explicitFactoryUsed = true;}if (!explicitFactoryUsed) {LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");//将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}definition.setLazyInit(lazyInitialization);}}
2.8 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
2.9 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}
 @Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}@Overridepublic Class<T> getObjectType() {return this.mapperInterface;}

注意:这里的getObject调用时机是,在创建的对象依赖了Mapper对象就会去创建该Mapper对象,此时通过MapperFactoryBean去获取

2.10 sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生

上面的getSqlSession()对应源码是:
在这里插入图片描述

2.11 MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean
2.12 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性

这一步是程序员自己定义一个SqlSessionFactory,例如:

@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
@ComponentScan("com.athome.tulin.springmybatis")
public class AppConfig {@Beanpublic SqlSessionFactory sqlSessionFactory() throws IOException {System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;}
}

这里定义的SqlSessionFactory 会赋值于2.10的sqlSessionTemplate

2.13 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

在这里插入图片描述

2.14 当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程

至此:业务类中的引入Mapper对象就复制成功。

 @Autowiredprivate OrderMapper orderMapper;

即:这时候的orderMapper就 是赋值了代理对象的对象是有值 的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中的软链接与硬链接

Linux链接概念 Linux链接分两种&#xff0c;一种被称为硬链接&#xff08;Hard Link&#xff09;&#xff0c;另一种被称为符号链接&#xff08;Symbolic Link&#xff09;。默认情况下&#xff0c;使用 ln 命令不加参数创建硬链接&#xff0c;加 -s 参数则创建软链接 硬链接…

【mongoDB】创建用户账号和权限

使用use database_name 命令创建或切换到一个数据库 查看用户 show users 输入该命令后&#xff0c;无数据表示该数据库没有用户 创建用户 user:" freedom " 表示用户名为freedom pwd:" 123456 ” 表示密码为123456 roles:[" root "] …

一键去除图片背景——background-removal-js

一些JavaScript库和工具可以帮助实现背景去除&#xff1a; OpenCV.js&#xff1a;OpenCV的JavaScript版本&#xff0c;提供了许多计算机视觉功能&#xff0c;包括背景去除。Jimp&#xff1a;一个用于处理图像的JavaScript库&#xff0c;提供了许多图像处理功能&#xff0c;包括…

Deepin基本环境查看(六)【计算机、回收站、控制面板、文档、设备管理器】

Deepin基本环境查看 - 目录Deepin基本环境查看&#xff08;一&#xff09;【基本信息】Deepin基本环境查看&#xff08;二&#xff09;【内存】Deepin基本环境查看&#xff08;三&#xff09;【网络信息】Deepin基本环境查看&#xff08;四&#xff09;【硬盘/分区、文件系统、…

鸿蒙ArkUI 宫格+列表+HttpAPI实现

鸿蒙ArkUI学习实现一个轮播图、一个九宫格、一个图文列表。然后请求第三方HTTPAPI加载数据&#xff0c;使用了axios鸿蒙扩展库来实现第三方API数据加载并动态显示数据。 import {navigateTo } from ../common/Pageimport axios, {AxiosResponse } from ohos/axiosinterface IDa…

第5章 (python深度学习——波斯美女)

第5章 深度学习用于计算机视觉 本章包括以下内容&#xff1a; 理解卷积神经网络&#xff08;convnet&#xff09; 使用数据增强来降低过拟合 使用预训练的卷积神经网络进行特征提取 微调预训练的卷积神经网络 将卷积神经网络学到的内容及其如何做出分类决策可视化 本章将…

Android 性能优化总结:包体积优化

前言 随着开发不断迭代&#xff0c;App体积越来越大&#xff0c;包大小的增大也会给我们应用带来其他的影响 比如 下载率影响 过大的包体积会影响下载转化率&#xff0c;根据Google Play Store包体积和转化率分析报告显示&#xff0c;平均每增加1M&#xff0c;转化率下降0.2%左…

基于Java SSM框架实现大学生综合素质评分平台项目【项目源码+论文说明】

基于java的SSM框架实现大学生综合素质评分平台演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所…

C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”

安装达梦数据库后&#xff0c;使用SqlSugar连接测试数据库并基于DBFirst方式创建数据库表对应的类&#xff0c;主要代码如下&#xff1a; SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){DbType DbType.Dm,ConnectionString "Serverlocalhost; User Id…

qemu单步调试arm64 linux kernel

一、背景和目的 qemu搭建arm64 linux kernel环境-CSDN博客 之前介绍了qemu启动kernel的配置步骤和方法&#xff0c;现在开始我们的调试&#xff0c;这篇文章主要讲解如何单步调试内核&#xff0c;所有的实验还是基于ARM64&#xff1b; 二、环境准备 需要准备hostx86 target…

容器化部署 Jenkins,并配置SSH远程操作服务器

目录 一、Jenkins是什么 二、常见的部署Jenkins的方法 三、为什么选择容器化部署 四、容器化部署Jenkins步骤 1、安装 Docker 2、获取 Jenkins 镜像 3、创建并运行容器 4、访问 Jenkins 4.1 查看初始密码问题 5、配置 Jenkins 5.1 安装插件 5.2 创建管理员用户 5.3…

Java项目实战--瑞吉外卖DAY03

目录 P22新增员工_编写全局异常处理器 P23新增员工_完善全局异常处理器并测试 p24新增员工_小结 P27员工分页查询_代码开发1 P28员工分页查询_代码开发2 P22新增员工_编写全局异常处理器 在COMMON新增全局异常捕获的类&#xff0c;其实就是代理我们这些controlle。通过aop把…

基于SSM+Shiro+Druid实现的企业资产后台管理系统

系统介绍 系统演示 关注【全栈小白】视频号查看演示视频 随着企业的发展&#xff0c;很多中小企业的规模越来越大&#xff0c;需要管理资产也越来越多&#xff0c;比如显示器&#xff0c;主机&#xff0c;打印机&#xff0c;传真机&#xff0c;复印机&#xff0c;电话&#…

SAP-PP: BOM选择标识不存在

在MM03 中选择生产版本无法打开 原因是未维护 BOM选择标识 配置路径&#xff1a;生产→物料需求计划→工厂参数→执行工厂参数总体维护→BOM/工艺路线选择 T-CD: OPPQ

flutter极光推送配置厂商通道(华为)笔记--进行中

一、基础集成按照下面官方文档进行 厂商通道相关参数申请教程 集成厂商 集成指南 官方文档&#xff1a;厂商通道回执配置指南 注意&#xff1a;不同厂商对app上架的要求不同&#xff0c;华为、荣耀 对app上架没有硬性要求 遇到问题 1、引入apply plugin: com.huawei.agconn…

Jenkins部署及应用

一. 简介 二. 下载地址源&#xff1a; …

Docker部署Stable-Diffusion-webui

前排提示&#xff1a;如果不想折腾&#xff0c;可直接跳到最后获取封装好的容器&#xff0c;一键运行 :D 前言 乘上AI生成的快车&#xff0c;一同看看沿途的风景。 启一个miniconda容器 docker run -itd -v 宿主机内SD项目路径:/tmp --gpus all --ipc host -p 7860:7860 con…

机器学习---可能近似正确(PAC)、出错界限框架

1. 计算学习理论概述 从理论上刻画了若干类型的机器学习问题中的困难和若干类型的机器学习算法的能力 这个理论要回答的问题是&#xff1a; 在什么样的条件下成功的学习是可能的&#xff1f; 在什么条件下某个特定的学习算法可保证成功运行&#xff1f; 这里考虑两种框架&…

《SPSS统计学基础与实证研究应用精解》视频讲解:数据结构重组(数据重组方式的选择、由变量组到样本观测值组的重组、由样本观测值组到变量组的重组)

《SPSS统计学基础与实证研究应用精解》4.11 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解4.11节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。本书旨在手把手教会…

2024年阿里云幻兽帕鲁Palworld游戏服务器优惠价格表

自建幻兽帕鲁服务器租用价格表&#xff0c;2024阿里云推出专属幻兽帕鲁Palworld游戏优惠服务器&#xff0c;配置分为4核16G和4核32G服务器&#xff0c;4核16G配置32.25元/1个月、10M带宽66.30元/1个月、4核32G配置113.24元/1个月&#xff0c;4核32G配置3个月339.72元。ECS云服务…