Mybatis3源码分析(二):扫描Mapper关联到spring IOC容器

Java后端开发学习路线:从Spring到AI赋能的实战指南 Java作为企业级应用开发的主流语言,其技术生态围绕JVM虚拟机、并发编程和集合框架等核心机制构建。理解这些底层原理是掌握Spring框架等高级技术的基础,后者通过控制反转和面向切面编程等设计模式实现了高效的组件管理。在微服务架构和分布式系统成为主流的今天,掌握缓存、消息队列和性能监控等技术对构建高可用应用至关重要。随着AI工具的普及,开发者现在可以利用智能辅助进行代码生成、问题排查和系统设计,显著提升开发效率。本文聚焦于Java后端技术栈的系统学习路径,并结合AI应用实践,为开发者提供从基础到高阶的完整成 阅读详情

    首先讲讲mapper是怎么从配置到对象的。

<!-- 采用自动扫描方式创建mapper bean(单个更新模式) -->  
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
    <property name="basePackage" value="com.xxx.dao" />  
    <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateSimple" />  
    <property name="markerInterface" value="com.xxx.dao.SimpleDao" />  
</bean>  

        实现了basePackage下所有实现了markerInterface指明的类的接口都会被扫描解析到。MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor(BeanDefinitionRegistry后置处理器,想想Spring AOP)接口,使其能在Spring ApplicationContext容器注册了所有BeanDefinition后调用它的postProcessBeanDefinitionRegistry方法。下面我们看看MapperScannerConfigurer是怎么实现这个方法的。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
      //处理一些.properties中配置的mybaits有关的属性,例如sqlSessionFactoryBeanName
      processPropertyPlaceHolders();
    }
    //创建类路径扫描器
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    //根据配置为scanner定义扫描规则
    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.registerFilters();
    //开始扫描,参数中使用了tokenizeToStringArray函数来处理多basePackage的情况一般是“;”分隔
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
     ClassPathMapperScanner类的父类ClassPathBeanDefinitionScanner的doScan函数:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		//BeanDefinitionHolder的容器
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
			//选择候选者
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				//如果是基包下的一个抽象类,我们配置的mapper接口如果不是注解方式也会走这处理
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				//如果基包下的类是有注解的
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
ClassPathMapperScanner的doScan函数:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
	  //调用父类扫描函数
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

        //此处的mapperInterface就是我们写的mapper接口一般是个空接口,实际上关联到它
        //的BeanDefinition中的Bean是MapperFactoryBean,MapperFactoryBean很重要
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        definition.setBeanClass(MapperFactoryBean.class);

        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) {
          if (logger.isDebugEnabled()) {
            logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
          }
          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
      }
    }

    return beanDefinitions;
  }

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		//如果spring中没注册这个beanName对应的bean那么就是mybaits需要的类,一般mybaits的mapper类是一个接口不会被spring实例化加载到IOC容器中
		if (!this.registry.containsBeanDefinition(beanName)) {
			return true;
		}
		//如果spring中有这个类的bean定义
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
			existingDef = originatingDef;
		}
		//如果重复扫描此类或者或者重复获取同一个类的源文件或者是其它类的子类则是不兼容的,这样的类是不符合mybaits加载要求返回false
		if (isCompatible(beanDefinition, existingDef)) {
			return false;
		}
		throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
				"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
				"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
	}

    我们再来看看MapperFactoryBean的checkDaoConfig函数,看它是怎么将DAO关联到对应的mapper.xml的:

protected void checkDaoConfig() {
	//调用父类同名方法
    super.checkDaoConfig();
    //获取sqlSessionFactoryBean中配置文件在java中的对象Configuration
    //如果没有mapperInterface的定义则加入此mapper的定义
    //此configuration对象是mybaits下的不是spring的哦!
    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Throwable t) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
        throw new IllegalArgumentException(t);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

     这样Mapper接口就被注册到sqlSession中,每当操作sqlSession时就会根据这个mapperInterface去查找对应的mapper.xml构建mapper从而完成数据库操作。

     经过以上过程mybaits找到了所有mapper并将其加载到了spring ioc容器里,本章也就完结了。下一章我们再讲mybaits是怎么解析mapper.xml的。




Java中级开发工程师面试核心考察维度与高频问题解析 Java作为企业级开发的主流语言,其并发编程和集合框架是面试必考重点。HashMap作为核心数据结构,JDK8通过引入红黑树优化了哈希冲突处理,与ConcurrentHashMap的线程安全实现形成对比。在并发领域,线程池参数配置和锁机制选择直接影响系统性能,合理使用CountDownLatch等工具类能有效协调多线程任务。Spring框架的IoC容器初始化过程和AOP代理机制体现了设计模式的精妙应用,而事务传播行为的理解对保证数据一致性至关重要。掌握这些原理不仅能应对技术面试,更能指导实际工程中的架构设计 阅读详情

相关推荐

Java面试题

JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。JRE(Java Runtime Environment),Java运行环境。JDK(Java Development Kit),Java开发工具包。如果重写了equals方法,equals比较的是对象的内容。如果没有重写equals,equals就相当于==;对于引用类型,==比较的是堆中内存对象的地址;对于基本类型,==比较的是栈中的值;

IAtlantiscsdn的博客 348

【MyBatis】Spring集成原理(二):分析注入 MapperScannerConfigurer

在上一篇我们分析了 Spring 中不再使用 DefaultSqlSession 因为它是线程不安全的,而是自己实现了一个线程安全 SqlSessionTemplate。按照编程式使用的套路,拿到 SqlSession 后就可以去操作数据库了,所以我们演示通过 SqlSessionDaoSupport 去获取 SqlSessionTemplate 操作数据库。 但是这么做仍然存在代码冗余(需要手动创建Mapper实现类去获取SqlSessionTemplate)和硬编码的问题(指定 StatementID,

菜鸡也有大佬梦 625

SageMath下载与安装

SageMath的下载与安装

旺旺的碎冰冰~的博客 7502

Mybatis源码之XXMapper.java的扫描,然后注册IOC容器

xxMapper的扫描,然后注册IOC容器 注解中@Import的类获取 先看注解中@Import的类是如何获取。首先启动类中有@SpringBootApplication、@MapperScan注解,其中在ConfigurationClassParser#doProcessConfigurationClass()中会进行对@Import的处理,去调用processImports方法。 在看processImports之前,先看下启动类@MapperScan有什么作用,MapperScan上面的注解@Imp

weixin_43843104的博客 288

【技术实战】SeaTunnel 实现 HTTP 到 Doris 数据同步的配置优化与问题解决

本文分享了使用SeaTunnel实现HTTP数据源到Doris数仓同步的实战经验。详细解析了从环境搭建、核心配置到生产部署的全流程,重点针对数据类型映射、分页拉取、依赖冲突等典型问题提供了解决方案,并给出了连接参数调优、错误处理等进阶优化技巧,帮助开发者构建稳定高效的数据同步管道。

gan8painter的博客 754

windows中如何将已安装的nodejs高版本降级为低版本

第一步:先清空本地安装的node.js版本 1.按健win+R弹出窗口,键盘输入cmd,然后敲回车(或者鼠标直接点击电脑左面最左下角的win窗口图标弹出,输入cmd再点击回车键) 然后进入命令控制行窗口,并输入where node查看之前本地安装的node的路径 2.找到上面找到的路径,将node.exe所在的父目录里面的所有东西都删除 3.为了彻底删除之前安装的node.js,鼠标点击电脑左面最左下角的win窗口图标弹出,输入“控制面板”再点击回车键进入,进入控制面板后,找到所安装的node.js并

weixin_44582077的博客 9万+

Springboot2是如何将mybatis mapper注入ioc

mybatis mapper 注入ioc

qq_35363433的博客 1203

UniApp实现多端支付功能完整实现指南

支付业务流程多端支付实现安全性保障异常处理最佳实践支付基础知识多端支付实现统一支付封装安全性处理异常处理最佳实践根据具体业务场景选择合适的支付方式注重安全性和用户体验做好异常处理和日志记录持续优化和改进支付流程关注我,持续分享前端项目开发经验!

weixin_63454527的博客 2128

Mybatis整合Spring

Mybatis整合Spring        根据官方的说法,在ibatis3,也就是Mybatis3问世之前,Spring3的开发工作就已经完成了,所以Spring3中还是没有对Mybatis3的支持。因此由Mybatis社区自己开发了一个Mybatis-Spring用来满足Mybatis用户整合Spring的需求。下面就将通过Mybatis-Spring来整合Mybatis跟Spring...

Elim的博客 385

基于微信小程序的智能门禁管理系统的设计与实现(文章+源码)

基于微信小程序门禁管理小程序,该系统支持用户注册登录、查阅新闻动态、提交开门申请和发布留言等功能,同时管理员可以进行用户管理、新闻信息管理、门禁审批管理和留言管理等操作。该系统将采用IDEA开发工具、SSM框架和MYSQL数据库等相关技术,实现用户登录注册、个人信息管理、门禁申请提交、管理员审核等功能。该系统的实现,可以有效提高门禁管理的效率和便利性,同时也具有较强的可扩展性和可维护性。本研究对于智能门禁管理系统的设计和实现具有一定的参考价值。

u011779145的博客 1399

3D打印机控制系统设计:基于MATLAB的3D打印机运动学建模、轨迹规划和控制

控制实现:在控制实现中,需要将轨迹规划的结果转化为实际的控制指令,控制3D打印机按照规划的轨迹进行运动。3D打印机的运动学建模:3D打印机的运动学建模是指将3D打印机的机械结构、传动系统和运动控制部件等抽象成数学模型,以便于进行控制系统的设计和分析。轨迹规划:轨迹规划是指根据3D模型的几何形状和打印机的运动学模型,计算出打印机需要执行的运动轨迹,以便于实现所需的打印效果。控制实现:控制实现是指将轨迹规划的结果转化为控制指令,控制3D打印机按照规划的轨迹进行运动。

走向CTO的路上... 2473

解决 mybatis mapper配置文件与接口名称必须一致问题

最近使用mybatis的时候一直很疑惑 为什么接口名必须与Mybatis的映射文件名一定要一模一样,如果不一样就会报如下错。

tiantiantbtb的博客 2172

STM32智能小车控制舵机转向

文章目录前言一、舵机简单介绍及接线方式二、cubeMX配置三、代码解析总结 前言 本文将带大家学习如何驱动舵机转向 一、舵机简单介绍及接线方式 信号线(黄线)红线(电源线)棕色(地线) 舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分,总间隔为2ms。以180度角度伺服为例,那么对应的控制关系是这样的: 0.5ms--------------0度; 1.0ms------------45度; 1.5ms------------90度; 2

m0_49476241的博客 8614

Mybatis-好文推荐

Mybatis-好文推荐 系列文章目录 Mybatis-原理 Mybatis-缓存 Mybatis-mapper文件中$与#的区别 Mybatis-和Hibernate对比 Mybatis-collection错误去重问题 Mybatis-好文推荐 0x01 使用 MyBatis3使用手册 MyBatis极速入门开发手册(2017-2-6更新) Mybatis传递多个参数的4种方式 fore...

迷路剑客个人博客 487

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

从零开始SpringCloud Alibaba实战(61)——Mybatis之结果映射

文章目录前言别名映射驼峰映射resultMap映射高级结果映射关联(association)例子关联的嵌套结果映射 前言 Myabtis中的结果映射有很多种方式,下面会逐一介绍。 别名映射 这个简单,保持查询的SQL返回的字段和Java实体类一样即可,比如SQL可以写成: <select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'> select user_id as userId, dept_id

liuerchong的博客 343

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【小家Spring】注意BeanPostProcessor启动时对依赖Bean的“误伤”陷阱(is not eligible for getting processed by all...)

前言 本篇博文和Spring的上下文启动有很强的关联性,同时需要读者对Spring中的BeanPostProcessor有较为熟悉的了解。若有不了解的同学,建议想点击一下相关阅读的文章列表,先进行个简单的了解~ 这是曾发生在我原公司工作中的一个Spring项目的真实场景案例:简单的描述就是在使用Spring整合@Async、security的时候,出现一个诡异的现象:我整合进security后原来...

架构师:通透,才能写出好代码! 10万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

从源码到项目:Django/SpringBoot学生图书管理系统实战解析

在软件开发领域,源码分析是理解系统架构和业务逻辑的核心技能。通过解析项目配置文件、数据模型和路由机制,开发者能够掌握从环境搭建到业务实现的完整流程。以学生图书管理系统为例,这一过程不仅涉及Django或SpringBoot框架的应用,更涵盖了数据库设计、API接口开发和系统部署等关键技术环节。源码的价值在于提供一个可迭代的样本,帮助学习者跨越从‘能运行’到‘能理解’的认知鸿沟,最终实现从项目复用到自主创新的能力提升。

weixin_30896511的博客 291

MyBatis接口(Bean)与配置信息(Mapper)绑定

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34268843的博客 813

spring源码剖析(八)spring整合mybatis原理

MyBatis相信很多人都会使用,但是当MyBatis整合到了spring中,我们发现在Spring中使用更加方便了。既然那么方便,Spring到底为我们做了哪些工作呢,它如何将MyBatis整合到Spring中的呢,Spring在整合MyBatis时候做了哪些封装,以及做了哪些拓展,又是怎么实现这些封装以及拓展的,让我们来打开这一部分的源代码,一探究竟。

Fighter168的专栏 2万+

Spring源码解析(一):扫描注解并放入IOC容器(呕心沥血之作,大家耐心看完,很有帮助!!!)

今日份重点掌握:扫描注解并放入IOC容器 我们先不谈怎么将外部的对象放入IOC容器,我们先讲一个问题,这个问题是什么呢?就是我们的注解来了之后,如何实现扫描,以及,最终是怎么将这个对象放到我们的容器中去的。 实际上,最终调用的是refresh这个方法(register先不管,后面会谈),我们先进入refresh这个方法 在refresh这个方法中(以下方法供了解) 重点来了!!!真正实现对象实例化的是这个方法: 也就是说,我们拿到这个对象,生成对象,放到我们的容器里面去

专注于优雅与性能的未来架构师 534

spring入门

spring 一, spring的基本应用 1. spring概述 1.1 什么是spring spring是一个分层的Java SE/EE full-stack(一站式)轻量级开源框架,它以IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为内核.使用基本的JavaBean来完成以前只可能由EJB(Enterprise Java Beans,java企业Bean)完成的工作,取代了EJB的臃肿,低效的开发模式. 1.

m0_55871462的博客 404

Annotation-specified bean name.. for bean class ...

org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'tmdOcrRecordMapper' for bean class [com.ndmicro.mybatis.ndmicro_tmd.mapper.TmdOcrRecordMapper] conflicts with existing, non-compatible bean definiti.

weixin_42169734的博客 3万+

mybatis+mybatisplus源码学习(一)MapperScannerConfigurer

前言 一、通过@MapperScan 1、进入@MapperScan可以看到,通过@Import导入了MapperScannerRegistrar.class @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { 2、MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar 在ConfigurationC

码易的博客 1231
上一篇: Mybatis3源码分析(一):从sqlSession说起
下一篇: Mybatis3源码分析(三):解析mapper的xml配置文件
闲庭细步
博客等级 码龄18年 304粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值