源码透析MapperScannerRegistrar和MapperScannerConfigurer的区别及作用

news2024/11/20 20:28:50

文章目录

    • 前言
    • MapperScannerRegistrar
      • 使用方式
      • 实现原理
    • MapperScannerConfigurer
      • 使用方式
      • 实现原理
    • 两者区别对比
    • 源码解析
      • MapperScannerRegistrar
      • MapperScannerConfigurer
      • MapperFactoryBean
    • 总结

本文里面涉及到的相关文章

@MapperScan注解里面涉及到的@Import注解解析可以查看系列文章

SpringBoot自动装配系列文章
@EnableXXX注解+@Import轻松实现SpringBoot的模块装配
带你拿捏SpringBoot自动装配的核心技术?模块装配(@EnableXXX注解+@Import)+ 条件装配(@ConditionalXXX)
深入探究Spring Boot自动配置原理及SPI机制:实现灵活的插件化开发

MapperFactoryBeanMapperScannerConfigurer相关文章

MyBatis系列相关相关文章
究竟FactoryBean是什么?深入理解Spring的工厂神器
超硬核解析Mybatis动态代理原理!只有接口没实现也能跑?
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
源码透析MapperScannerRegistrar和MapperScannerConfigurer的区别及作用

在这里插入图片描述

前言

在使用Spring Boot和MyBatis整合的时候,我们经常会看到@MapperScan这个注解,它的作用是扫描指定包下的Mapper接口,并将它们注册到Spring容器中,这样我们就可以在Service层或者Controller层直接注入Mapper接口的实例,而不需要写DAO层的实现类。那么,@MapperScan这个注解是如何实现这个功能的呢?它背后涉及到了两个重要的类:MapperScannerRegistrarMapperScannerConfigurer,它们之间有什么区别和联系呢?

MapperScannerRegistrar

使用方式

一种典型的使用方式是在 Spring Boot 的启动类或者配置类上添加 @MapperScan 注解:

@Configuration
@MapperScan(basePackages = "com.apple.mapper")
public class MyAppConfig {
    // ...
}

点开@MapperScan注解,我们又可以看到它使用 了@Import

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

关于@Import的使用解析,我再之前的文章已有提到


@EnableXXX注解+@Import轻松实现SpringBoot的模块装配

@EnableXXX注解+@Import轻松实现SpringBoot的模块装配

@EnableXXX注解+@Import轻松实现SpringBoot的模块装配


实现原理

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,我们在@MapperScan注解中使用 @Import 注解把它导入到配置类中,在运行时由 Spring 处理 @Import 标注的类,从而实现动态添加 BeanDefinition 到 Spring 容器的目的。

在实际的应用中, @MapperScan 注解结合 MapperScannerRegistrar 一起工作。@MapperScan 负责定义扫描路径,而 MapperScannerRegistrar 负责将这些配置注册成 BeanDefinition

MapperScannerConfigurer

使用方式

在非 Spring Boot 的 Spring 应用或者希望通过 XML 来配置扫描路径的场合更常见。以下是一个 XML 配置示例:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.apple.mapper" />
     <!-- 其他可配置项,例如 sqlSessionFactoryBean 名称 -->
</bean>

通过配置 basePackage 属性,我们告诉 MapperScannerConfigurer 要扫描哪个包。

而实际上,当使用 Spring boot,并结合 @MapperScan 注解配置 MapperScannerRegistrar 的时候,Mybatis-spring-boot-starter 会自动配置 SqlSessionFactorySqlSessionTemplate,进一步简化配置。

实现原理

MapperScannerConfigurer 则实现了 BeanDefinitionRegistryPostProcessor 接口,它在 BeanFactory 的标准初始化过程中修改内部的 bean 定义或者增加额外的 bean 定义。该类通常通过 XML 配置方式使用,它需要在 Application Context 配置中明确声明。

两者区别对比

总体上,MapperScannerRegistrar 更适合用于 Spring Boot 或者基于 Java Config 的配置场景,而 MapperScannerConfigurer 更常用于基于 XML 配置的传统 Spring 应用环境中。若需要基于不同的环境或者个人喜好选择使用哪种方式,需要确保配置步骤的正确实施。

由于 MapperScannerConfigurer 是在 Bean 加载完成后初始化的,因此它能够更好地与 Spring 的生命周期集成。但是这也带来了一个弊端,就是在任何使用 @Autowired 注入 Mapper 前,必须确保 MapperScannerConfigurer 已经初始化完成,这可能会影响到一些早期的 Bean 的使用。

源码解析

MapperScannerRegistrar

在这里插入图片描述

注意不同版本的mybatis-spring 整合依赖包里面对应的MapperScannerRegistrar会有所区别,但是做的事情都是一样的!

MapperScannerRegistrar是一个实现了ImportBeanDefinitionRegistrar接口的类,它的作用是在Spring容器启动的时候,根据@MapperScan注解的配置,动态地注册Mapper接口的BeanDefinition对象到Spring容器中。它的核心方法是registerBeanDefinitions,它的代码如下:

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  // 获取@MapperScan注解的所有属性值
  AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  if (mapperScanAttrs != null) {
    // 注册MapperScannerConfigurer的BeanDefinition对象
    registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
  }
}

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {

  // 创建一个GenericBeanDefinition对象,用来存储MapperScannerConfigurer的相关属性
  GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  beanDefinition.setBeanClass(MapperScannerConfigurer.class);
  beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  beanDefinition.setSynthetic(true);

  // 获取@MapperScan注解的各个属性值,包括basePackages, annotationClass, markerInterface, factoryBean等
  AnnotationAttributes attributes = AnnotationAttributes.fromMap(annoMeta.getAnnotationAttributes(MapperScan.class.getName()));
  List<String> basePackages = new ArrayList<>();
  basePackages.addAll(Arrays.stream(attributes.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
  basePackages.addAll(Arrays.stream(attributes.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
  basePackages.addAll(Arrays.stream(attributes.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));

  // 将@MapperScan注解的属性值赋给MapperScannerConfigurer对象的属性
  beanDefinition.getPropertyValues().add("processPropertyPlaceHolders", true);
  beanDefinition.getPropertyValues().add("annotationClass", attributes.getClass("annotationClass"));
  beanDefinition.getPropertyValues().add("markerInterface", attributes.getClass("markerInterface"));
  beanDefinition.getPropertyValues().add("factoryBean", attributes.getClass("factoryBean"));
  beanDefinition.getPropertyValues().add("basePackages", basePackages);

  // 将MapperScannerConfigurer的BeanDefinition对象注册到Spring容器中,beanName是根据@MapperScan注解所在的类的名称和序号生成的
  registry.registerBeanDefinition(beanName, beanDefinition);

}

从上面的代码可以看出,MapperScannerRegistrar的作用是创建一个MapperScannerConfigurer的BeanDefinition对象,并将它注册到Spring容器中,同时将@MapperScan注解的属性值赋给MapperScannerConfigurer对象的属性,这样就完成了@MapperScan注解的解析和注册工作。那么,MapperScannerConfigurer又是什么呢?

MapperScannerConfigurer

更为详细的MapperScannerConfigurer解析可以查看我之前的文章


Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析

Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析

Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析


MapperScannerConfigurer是一个实现了BeanDefinitionRegistryPostProcessor接口的类,它的作用是在Spring容器初始化完成后,扫描指定包下的Mapper接口,并将它们创建成MapperFactoryBean的BeanDefinition对象,然后注册到Spring容器中。它的核心方法是postProcessBeanDefinitionRegistry,它的代码如下:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  // 如果已经执行过,就跳过
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  // 创建一个ClassPathMapperScanner对象,用来扫描指定包下的Mapper接口
  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

  // 设置扫描器的相关属性,包括注解过滤器,基础包,工厂类等
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }
  scanner.setMapperFactoryBeanCustomizer(this.mapperFactoryBeanCustomizer);

  // 执行扫描操作,将扫描到的Mapper接口创建成MapperFactoryBean的BeanDefinition对象,并注册到Spring容器中
  scanner.registerFilters();
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackages, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

从上面的代码可以看出,MapperScannerConfigurer的作用是创建一个ClassPathMapperScanner对象,并设置它的相关属性,然后调用它的scan方法,扫描指定包下的Mapper接口,并将它们创建成MapperFactoryBean的BeanDefinition对象,并注册到Spring容器中。这样,当Spring容器初始化完成后,就可以根据这些BeanDefinition对象,创建出Mapper接口的代理对象,并注入到其他的Bean中,实现Mapper接口的自动注入功能。那么,MapperFactoryBean又是什么呢?它又有什么作用呢?我们接下来看看。

更为详细的Reconfigurer解析可以查看我之前的文章


Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析

Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析

Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析


MapperFactoryBean

更为详细的MapperFactoryBean解析可以查看我之前的文章


Mybatis与Spring结合深探——MapperFactoryBean的奥秘

Mybatis与Spring结合深探——MapperFactoryBean的奥秘

Mybatis与Spring结合深探——MapperFactoryBean的奥秘


在这里插入图片描述

MapperFactoryBean是一个实现了FactoryBean接口的类,它的作用是创建Mapper接口的代理对象,并将它返回给Spring容器。它的核心方法是getObject,它的代码如下:

@Override
public T getObject() throws Exception {
  // 如果已经创建过代理对象,就直接返回
  if (this.mapperProxy != null) {
    return this.mapperProxy;
  }

  // 从SqlSessionTemplate或者SqlSessionFactory中获取SqlSession对象
  SqlSession sqlSession = getSqlSession();

  // 创建Mapper接口的代理对象,使用了MyBatis的MapperProxyFactory类
  this.mapperProxy = new MapperProxyFactory<>(this.mapperInterface).newInstance(sqlSession);
  return this.mapperProxy;
}

从上面的代码可以看出,MapperFactoryBean的作用是从SqlSessionTemplate或者SqlSessionFactory中获取SqlSession对象,然后使用MyBatis的MapperProxyFactory类,创建Mapper接口的代理对象,并将它返回给Spring容器。这样,当我们在Service层或者Controller层注入Mapper接口的实例时,实际上注入的是MapperFactoryBean创建的代理对象,这个代理对象会拦截Mapper接口的方法调用,并执行相应的SQL语句,完成持久层的操作。

更为详细的MapperFactoryBean解析可以查看我之前的文章


Mybatis与Spring结合深探——MapperFactoryBean的奥秘

Mybatis与Spring结合深探——MapperFactoryBean的奥秘

Mybatis与Spring结合深探——MapperFactoryBean的奥秘


总结

  • MapperScannerRegistrar是一个实现了ImportBeanDefinitionRegistrar接口的类,它的作用是在Spring容器启动的时候,根据@MapperScan注解的配置,动态地注册MapperScannerConfigurer的BeanDefinition对象到Spring容器中,同时将@MapperScan注解的属性值赋给MapperScannerConfigurer对象的属性。
  • MapperScannerConfigurer是一个实现了BeanDefinitionRegistryPostProcessor接口的类,它的作用是在Spring容器初始化完成后,扫描指定包下的Mapper接口,并将它们创建成MapperFactoryBean的BeanDefinition对象,然后注册到Spring容器中。
  • MapperFactoryBean是一个实现了FactoryBean接口的类,它的作用是创建Mapper接口的代理对象,并将它返回给Spring容器。

这三个类之间的关系可以用下图表示:

@MapperScan -> MapperScannerRegistrar -> MapperScannerConfigurer -> ClassPathMapperScanner -> MapperFactoryBean -> MapperProxy

通过这个流程,我们就可以实现Mapper接口的自动扫描和注入,简化了持久层的开发,提高了开发效率。当然,这个流程还涉及到了很多其他的细节和原理,比如MyBatis的MapperProxyFactory和MapperProxy类,Spring的ImportBeanDefinitionRegistrar和BeanDefinitionRegistryPostProcessor接口,以及Spring和MyBatis的整合原理等。相关文章可以参考我开头列举的系列文章,更为系统的进行学习!!!

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

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

相关文章

Flutter:跨平台移动应用开发的未来

Flutter&#xff1a;跨平台移动应用开发的未来 引言 Flutter的背景和概述 Flutter是由Google开发的一个开源UI工具包&#xff0c;用于构建漂亮、快速且高度可定制的移动应用程序。它于2017年首次发布&#xff0c;并迅速引起了开发者们的关注。Flutter采用了一种全新的方法来…

pip install flagai时出现Collecting PyYAML==5.4.1 (from flagai)等错误信息的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

7.【CPP】String类

一.汉字的编码 我们知道计算机存储英文字母&#xff0c;标点&#xff0c;数字用的是ascall码&#xff0c;128种用一个字节表示绰绰有余。而汉字远远不止128种&#xff0c;因此汉字需要两个字节表示。 1.gbk编码中汉字占两个字节。 2.utf-8中&#xff0c;一个汉字占三个字节。…

Oracle Linux 6.10 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;本次安装图解是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

第十回 朱贵水亭施号箭 林冲雪夜上梁山-FreeBSD/Linux 控制台基础操作

林冲被众庄客捉住&#xff0c;吊在门楼下&#xff0c;正被打时&#xff0c;柴进来了&#xff0c;赶快把林冲救下来。原来这是柴进打猎用的小庄子&#xff0c; 林冲就把火烧草料场一事跟柴进详细的说了。柴进说兄弟真是命运多磨难啊。林冲住了几日&#xff0c;恐怕连累柴进&…

Spring+SprinMVC+MyBatis配置方式简易模板

SpringSprinMVCMyBatis配置方式简易模板代码Demo GitHub访问 ssm-tpl-cfg 一、SQL数据准备 创建数据库test&#xff0c;执行下方SQL创建表ssm-tpl-cfg /*Navicat Premium Data TransferSource Server : 127.0.0.1Source Server Type : MySQLSource Server Versio…

网络编程 day6

网络聊天室项目 1.服务器端 #include <head.h> #define SER_IP "192.168.125.11" #define SER_PORT 6666 typedef struct Msg {char user[32]; //用户名int type; //1.登录、2.发消息、0.退出char text[1024]; //消息 } msg_t; typedef struct List…

Vue diff原理

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

Med-YOLO:3D + 医学影像 + 检测框架

Med-YOLO&#xff1a;3D 医学影像 检测框架 提出背景设计思路网络设计训练设计讨论分析 魔改代码&#xff1a;加强小目标检测总结 提出背景 论文链接&#xff1a;https://arxiv.org/abs/2312.07729 代码链接&#xff1a;https://github.com/JDSobek/MedYOLO 提出背景&…

助力焊接场景下自动化缺陷检测识别,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建工业焊接场景下工件表面焊接裂纹缺陷检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在工件表面焊接场景下常常有对工件表面缺陷智能自动化检测识别的需求&#xff0c;工业AI结合落地是一个比较有潜力的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行…

一周时间,开发了一款封面图生成工具

介绍 这是一款封面图的制作工具&#xff0c;根据简单的配置即可生成一张好看的封面图&#xff0c;目前已有七款主题可以选择。做这个工具的初衷来自平时写文章&#xff0c;都为封面图发愁&#xff0c;去图片 网站上搜索很难找到满意的&#xff0c;而且当你要的图如果要搭配上文…

【OCR项目】之用HALCON的深度学习工具进行文字识别,并导出到C++调用

前言 HALCON是一个强大的机器视觉工具&#xff0c;包含了2D&#xff0c;3D图像各种算子&#xff0c;以及各种任务的深度学习工具&#xff0c;包括目标检测&#xff0c;实例分割&#xff0c;文字识别等。 这次从实际生产的角度&#xff0c;来分享一下如何用HALCON进行文字识别…

基于 IDEA 创建 Maven 工程

1. 概念梳理Maven工程的GAVP Maven工程相对之前的项目&#xff0c;多出一组gavp属性&#xff0c;gav&#xff08;表示当前工程的坐标&#xff09;需要我们在创建项目的时候指定&#xff0c;p&#xff08;表示打包方式&#xff09;有默认值&#xff08;默认为 jar 包&#xff0…

web架构师编辑器内容-拖动元素改变元素的位置和大小的完成

拖动移动元素 改变编辑器的定位系统 我们目前的元素都是按照块级元素直接自上而下的排列在画布中&#xff0c;为了让元素实现精确的定位和调整&#xff0c;我们需要改变这些元素的定位实现。我们需要让这些元素画布区域来进行绝对定位。如果我们有一个元素有这些已经保存的 c…

第15届蓝桥杯嵌入式省赛准备第三天总结笔记(使用STM32cubeMX创建hal库工程+串口接收发送)

因为我是自己搞得板子&#xff0c;原本的下程序和串口1有问题&#xff0c;所以我用的是串口2&#xff0c;用的PA2和PA3 一&#xff0c;使用CubeMX配置串口 选择A开头的这个是异步通信。 配置串口参数&#xff0c;往届的题基本用的9600波特率&#xff0c;所以我这里设置为9600…

【Linux】Ubuntu的gnome切换KDE Plasma

文章目录 安装KDE Plasma桌面环境添加软件源并更新apt安装kubuntu-desktop&#xff08;作者没有成功&#xff09;aptitude安装kubuntu-desktop多次aptitude install&#xff08;特别重要特别重要&#xff09;其他kde软件包 卸载gnome桌面 Ubuntu自带的桌面环境是gnome&#xff…

cuda二进制文件中到底有些什么

大家好。今天我们来讨论一下&#xff0c;相比gcc编译器编译的二进制elf文件&#xff0c;包含有 cuda kernel 的源文件编译出来的 elf 文件有什么不同呢&#xff1f; 之前研究过一点 tvm。从 BYOC 的框架中可以得知&#xff0c;前端将模型 partition 成 host 和 accel(accel 表…

《WebKit 技术内幕》之六(2): CSS解释器和样式布局

2 CSS解释器和规则匹配 在了解了CSS的基本概念之后&#xff0c;下面来理解WebKit如何来解释CSS代码并选择相应的规则。通过介绍WebKit的主要设施帮助理解WebKit的内部工作原理和机制。 2.1 样式的WebKit表示类 在DOM树中&#xff0c;CSS样式可以包含在“style”元素中或者使…

【QT+QGIS跨平台编译】之四:【libSSH2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libSSH2介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、libSSH2介绍 libSSH2是一个开源的C函数库&#xff0c;用来实现SSH2协议。 SSH(Secure SHell)到目前为止有两个不兼容的版本——SSH1和SSH2。 SSH2避免了RSA的专利问题&#xff0c;并修补了CRC…

C#winform上位机开发学习笔记2-串口助手的定时发送功能添加

1.功能描述 选择自动发送功能后&#xff0c;按照设定的发送时间发送数据 2.代码部分 增加计时器空间Timer 使能计时器&#xff0c;默认设置定时时间为1秒 组合框设置默认复选信息 编写选择框事件函数 //自动发送事件private void checkBox27_CheckedChanged(object sender, E…