Mybatis执行sql流程(二)之加载Mapper

Mybatis加载Mapper

注册方式注册时机特点
@MapperScanBean定义阶段注册接口定义批量注册,推荐方式
@Mapper (接口注解)@MapperScan需每个接口单独标注
XML 配置 <mapper>MyBatis 初始化时传统方式,不依赖 Spring 容器
SqlSessionTemplate 直接获取调用时编程式获取,不自动注册为 Bean

@MapperScan注册mapper

// 使用:在启动类上添加注解
@MapperScan("com.zy.**.mapper")
@Import(MapperScannerRegistrar.class) // 此处使用了@Import注解,用于导入其他配置
@Repeatable(MapperScans.class)
public @interface MapperScan {...;
}
/**
1.指示要导入的一个或多个组件,通常是@Configuration类。
2.提供与Spring XML中的<import/>元素等效的功能。
3.允许导入@Configuration类,ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从4.2开始,类似于AnnotationConfigApplicationContext.register)。
4.导入的@Configuration类中声明的@Bean定义应该使用@Autowired注入来访问。也可以自动连接声明bean的配置类实例。
5.如果需要导入XML或其他非@Configuration bean定义资源,请改用@ImportResource注解。
*/public @interface Import {/*** {@link Configuration @Configuration}, {@link ImportSelector},* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.*/Class<?>[] value();}
// 实现了ImportBeanDefinitionRegistrar接口,此处涉及到Spring加载BeanDefinition的逻辑
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;// 重写ImportBeanDefinitionRegistrar 接口中的registerBeanDefinitions 方法@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 1. 获取@MapperScan注解属性AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));// 2. 实际注册逻辑if (mapperScanAttrs != null) {registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}}/**annoMeta 被注解类的元数据annoAttrs @MapperScan 注解的属性值registry : Spring bean 定义注册器beanName :要注册的 Bean 名称*/void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {
// 创建MapperScannerConfigurer 的通用Bean定义构建器。此处还只是构造builder。BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);// 启用属性占位符处理(如 ${jdbc.url})builder.addPropertyValue("processPropertyPlaceHolders", true);// 注解类过滤:如果设置了annotationClass(如@Mapper),则只扫描带该注解的接口Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {builder.addPropertyValue("annotationClass", annotationClass);}
// 标记接口过滤:如果设置了 markerInterface, 则只扫描实现该接口的MapperClass<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {builder.addPropertyValue("markerInterface", markerInterface);}
// Bean 名称生成器Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");if (!BeanNameGenerator.class.equals(generatorClass)) {builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));}
// 工厂类覆盖Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);}
// SQL 会话模板引用:支持指定特定的 SqlSessionTemplate BeanString sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");if (StringUtils.hasText(sqlSessionTemplateRef)) {builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));}
// SQL 会话工厂引用String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");if (StringUtils.hasText(sqlSessionFactoryRef)) {builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));}List<String> basePackages = new ArrayList<>();// 添加 value 属性值(包路径)basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加 basePackages 属性值basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加basePackageClasses的包路径basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
// 使用默认配置类所在包if (basePackages.isEmpty()) {basePackages.add(getDefaultBasePackage(annoMeta));}
// 延迟初始化String lazyInitialization = annoAttrs.getString("lazyInitialization");if (StringUtils.hasText(lazyInitialization)) {builder.addPropertyValue("lazyInitialization", lazyInitialization);}
// Bean 作用域String defaultScope = annoAttrs.getString("defaultScope");if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {builder.addPropertyValue("defaultScope", defaultScope);}
// 设置扫描包路径(逗号分割)builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));// for spring-nativebuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册Bean 定义。上述都是在配置builder, 此处才开始注册bean definitionregistry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
}

MapperScannerConfigurer 作为BeanDefinitionRegistryPostProcessor执行, 中重写了 postProcessBeanDefinitionRegistry 方法,最终调用doScan(basePackages);扫描Mapper

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {// 1. 调用父类扫描方法获取Bean定义Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {logger.warn("No MyBatis mapper was found...");} else {// 2. 处理扫描到的Bean定义processBeanDefinitions(beanDefinitions);}return beanDefinitions;}
// 省略部分代码private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {for (BeanDefinitionHolder holder : beanDefinitions) {GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();// 3. 获取Mapper接口名String mapperClassName = definition.getBeanClassName();// 4. BeanClass 转换为 MapperFactoryBean// 关键步骤1:添加构造参数(原始Mapper接口)definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName);try {// 关键步骤2:设置 MapperInterface 属性(Spring Native 兼容)definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));} catch (ClassNotFoundException ignore) {// ignore}// 关键步骤3:替换 Bean 类为 MapperFactoryBeandefinition.setBeanClass(this.mapperFactoryBeanClass);// 省略部分SqlSessionFactory 、sqlSessionTemplate相关内容。优先级:SqlSessionTemplate > SqlSessionFactory// 5. 设置自动装配模式:当未显示配置工厂/模板时,启用按类型自动装配if (!explicitFactoryUsed) {LOGGER.debug("Enabling autowire by type...");definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}}}
}

转换前Bean定义:

BeanDefinition {beanClass = com.example.UserMapper // 原始接口scope = singleton...
}

转换后 Bean 定义

BeanDefinition {beanClass = MapperFactoryBean // 替换为工厂类constructorArgs = [com.example.UserMapper] // 原始接口作为构造参数properties = {sqlSessionFactory = ref("sqlSessionFactory") // 自动装配addToConfig = true}...
}

实际效果:

// 用户代码
@Autowired
UserMapper userMapper; // 实际发生
MapperFactoryBean factoryBean = new MapperFactoryBean(UserMapper.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory);
UserMapper proxy = factoryBean.getObject(); // 返回 MyBatis 代理
// MapperFactoryBean 的 getObject() 
@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface); // 此处就是获取的代理类对象 }
// 从 @Autowired 到factoryBean.getObject(); 的过程:
@Autowired-> AutowiredAnnotationBeanPostProcessor.postProcessProperties()-> InjectionMetadata.inject()-> DefaultListableBeanFactory.doGetBean()-> AbstractBeanFactory.getObjectForBeanInstance(),发现 MapperFacoryBeanUserMapper 类型的工厂-> AbstractBeanFactory..getObjectFromFactoryBean()-> FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()-> FactoryBean<?>.getObject()
@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理需要注入UserMappergetBean("userMapper")doGetBean("userMapper")getObjectForBeanInstance()doGetObjectFromFactoryBean()getObject()sqlSession.getMapper()MapperProxy返回代理返回代理返回代理注入代理对象@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理

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

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

相关文章

基于 JSP+Mysql实现MVC房屋租赁系统

基于 MVC 的房屋租赁系统的设计与实现摘 要&#xff1a;房屋租赁管理系统与网络相结合&#xff0c;给用户提供更加周到和人性化的服务。网站模式为 MVC 模式&#xff0c;基于 MySQL 数据库,采用 JSP&#xff0c;Session 绘画跟踪、JavaScript 等技术,实现了普通用户可以浏览、查…

第六天~提取Arxml中CAN采样点信息Creat_ECU--Standard

⏱️ ARXML探秘:解码CAN采样点的精准艺术 在汽车电子的交响乐中,CAN采样点(Sample Point) 如同指挥家挥棒的关键时刻——它决定了何时"聆听"总线上的信号。这个看似微小的百分比数值,却是保障整车通信可靠性的核心密码。本文将带您深入ARXML中的采样点配置世界…

Windows Git安装配置

进入git官网Git - Downloading Package 点击下载&#xff08;可复制链接到迅雷&#xff09; 双击运行exe安装包 选择安装目录 下一步 选择 Git 默认编辑器&#xff0c;下一步设置初始化新项目(仓库)的主干名字 让Git决定&#xff08;Let Git decide&#xff09;使用默认的…

面试经验分享-某电影厂

java会吗&#xff1f;不会。。。。。hdfs读文件写文件的流程 数据写入 1-客户端向NameNode发起请求 2-NameNode审核权限和剩余空间&#xff0c;满足条件即允许写入&#xff0c;并告知客户端写入的DataNode地址 3-客户端向指定的DataNode发送数据包 4-被写入数据的DataNode同时完…

数据清理后续

前篇&#xff1a;Python 项目里的数据清理工作&#xff08;数据清洗步骤应用&#xff09; 一&#xff0c;先接上文添加两种数据填充的方法 1、线性回归填充 def lr_train_fill(train_data,train_label):train_data_all pd.concat([train_data, train_label], axis1)train_d…

nuc设置脚本开机自启动

在终端执行gnome-session-properties这个是带有图型化页面的设置开机自启动的软件没有这个软件的可以直接下载sudo apt update sudo apt install gnome-startup-applications一般都有&#xff0c;ubunutu自带的右边有添加&#xff0c;名称和注释随便写&#xff0c;只需要把命令…

JavaScript 性能优化实战大纲

JavaScript 性能优化实战大纲 核心优化方向 减少主线程阻塞 避免长任务&#xff08;Long Tasks&#xff09;拆分计算密集型操作使用 Web Workers 处理后台任务优先使用 requestIdleCallback 或 requestAnimationFrame 内存管理 避免内存泄漏&#xff08;如未清理的定时器、闭包…

openssl生成自签名证书的方法

因为开发中查询过各种命令&#xff0c;失败过很多次&#xff0c;所以记录一下正确的命令&#xff1a; 生成私钥-不要密码 openssl genpkey -algorithm RSA -out ssl/key.pem 生成自签名证书 openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -…

电影购票+票房预测系统 - 后端项目介绍(附源码)

电影购票预测系统 - 后端项目介绍 项目概述 本项目是一个基于Spring BootVue的前后端分离电影购票系统&#xff0c;包含完整的前台用户功能和后台管理功能&#xff0c;并提供数据可视化和电影预测功能。本文档将详细介绍后端项目的架构、功能模块、技术栈和使用方法。 项目源…

专利服务系统平台|个人专利服务系统|基于java和小程序的专利服务系统设计与实现(源码+数据库+文档)

专利服务系统平台 目录 基于java和小程序的专利服务系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&#x…

【HTML】3D动态凯旋门

目录 版本1.0&#xff1a;简易版本 版本2.0&#xff1a;建筑渲染 版本3.0&#xff1a;优化建筑群 版本4.0&#xff1a;增加公路和车流 版本5.0&#xff1a;去除压在公路上的建筑 版本6.0&#xff1a;优化车流群 版本7.0&#xff1a;添加烟花效果 版本8.0&#xff1a;添…

(论文阅读)FedViT:边缘视觉转换器的联邦持续学习

FedViT&#xff1a;边缘视觉转换器的联邦持续学习 FedViT: Federated continual learning of vision transformer at edge (北京理工大学-2023年发表于《Future Generation Computer Systems》中科院二区) highlight&#xff1a; •提出一种轻量级的客户端联合持续学习方法。 •…

微算法科技(NASDAQ: MLGO)研究利用PBFT中的动态视图变换机制,实现区块链系统高效运转

随着区块链技术的飞速发展&#xff0c;其去中心化、透明性、不可篡改等特性使得它在金融、供应链管理、物联网等多个领域得到了广泛应用。然而&#xff0c;区块链系统在高并发场景下的性能瓶颈问题一直是制约其大规模应用的关键因素。传统的共识算法如PoW&#xff08;工作量证明…

从数据汇总到高级分析,SQL 查询进阶实战(下篇)—— 分组、子查询与窗口函数全攻略

引言&#xff1a;从 “提取数据” 到 “洞察价值”&#xff0c;SQL 进阶之路 在掌握了基础查询与多表关联后&#xff0c;你是否曾遇到这样的挑战&#xff1a;如何按部门统计平均薪资&#xff1f;怎样找出每个岗位薪资最高的员工&#xff1f;或者如何计算销售额的月度环比增长率…

Spring 和 Lettuce 源码分析 Redis 节点状态检查与失败重连的工作原理

关键步骤&#xff1a;Spring Boot 启动时创建 LettuceConnectionFactory根据配置类型&#xff08;集群/哨兵/单机&#xff09;初始化客户端对于集群模式&#xff1a;创建 RedisClusterClient调用 setOptions(getClusterClientOptions(configuration)) 应用配置2. 节点状态检查机…

从ChatGPT到智能助手:Agent智能体如何颠覆AI应用

从ChatGPT到智能助手&#xff1a;Agent智能体如何颠覆AI应用 更多大模型知识分享&#xff0c;尽在>>>GitHub<<< Agent 智能体是什么 简单来说&#xff0c;Agent 智能体是一种能够感知环境&#xff0c;并根据自身目标自主采取行动的智能实体。它就像是一个拥…

Spring Boot应用实现图片资源服务

在这篇文章中&#xff0c;我们将介绍如何使用Spring Boot创建一个REST API来提供服务器上的静态图片资源。该API包括路径安全检查、文件存在性验证以及缓存控制等功能&#xff0c;并且代码包含详细的注释以帮助理解。Maven依赖 首先&#xff0c;在您的pom.xml文件中添加以下依赖…

Word 中 MathType 公式编号问题与解决

注&#xff1a;本文为 “Word 中 MathType 公式编号” 相关合辑。 图片清晰度受引文原图所限。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 【Word】解决 MathType 已插入公式按新章节开始编号的问题 Allan326 于 2020-03-25 15:30:08 发布 问题…

19. 大数据-产品概念

文章目录前言一、数据库1. 简介2. 使用场景3. 数据库类型4. 数据类型二、数据仓库1. 简介2. 使用场景3. 数据仓库架构三、数据平台1. 简介2. 使用场景3. 数据仓库架构四、数据中台1. 简介2. 使用场景3. 数据中台架构五、数据湖1. 简介2. 使用场景3. 数据湖架构六、总结1. 区别2…

python学习DAY46打卡

DAY 46 通道注意力(SE注意力) 内容&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插入的位置通道注意…