当前位置: 首页 > news >正文

mybtias集成spring原理?--spring,mybatis源码解析

最开始学ssm一套的时候,其实有几个问题:
1.为啥mybatis可以写个@Mapper就能注入接口了,我们实际调用的不是对象吗?
2.为啥mybatis有些甚至@Mapper都不用写,通过@MapperScan就行?
下面按 问题 1 & 2 拆成两条时间线,用 Spring 源码级链路“@Mapper 怎么注册成 Bean”“@MapperScan 怎么显式扫描” 说透。


源码流程分析:

注册钩子路径

第一步@EnableAutoConfiguration

@EnableAutoConfiguration 内部是:@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 是一个 DeferredImportSelector,在 Spring Boot 启动早期 被调用,它负责把 spring.factories 里的所有 EnableAutoConfiguration 键值读进来。

第二步:读取 mybatis实现的spring.factories。(jar 内自带,非用户手写)
mybatis-spring-boot-autoconfigure-2.2.x.jar
└─ META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

因此 MybatisAutoConfiguration BeanDefinition 自动被加载,无需用户写 @Import。

第三步:MybatisAutoConfiguration 注册扫描器
@Configuration(proxyBeanMethods = false) // 👈 这个空类被扫描原因,在 Spring 的 ConfigurationClassPostProcessor 阶段,
任何带有 @Configuration 的类都会进入 ConfigurationClassParser@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
@Import(AutoConfiguredMapperScannerRegistrar.class)   // 👈 关键钩子,实现了ImportBeanDefinitionRegistrar的registerBeanDefinitions
public static class MapperScannerRegistrarNotFoundConfiguration {}

@ConditionalOnMissingBean 只在容器里找不到 MapperFactoryBean/MapperScannerConfigurer 时生效。
@Import(AutoConfiguredMapperScannerRegistrar.class) 把 AutoConfiguredMapperScannerRegistrar 注册进来。
其中AutoConfiguredMapperScannerRegistrar s实现了ImportBeanDefinitionRegistrar的registerBeanDefinitions,扫描时候会判断是否实现该接口,然后将其添加进回调里面。

spring run调用链路:

SpringApplication.run└ AbstractApplicationContext.refresh()ConfigurationClassPostProcessor.processConfigBeanDefinitions()ConfigurationClassParser.parse(...)ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(...)loadBeanDefinitionsFromRegistrars(...)└ registrar.registerBeanDefinitions(...)
//ConfigurationClassParser.class的processImports方法,处理Import注解
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);//添加进importBeanDefinitionRegistrars,ConfigurationClassBeanDefinitionReader 调用configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}//ConfigurationClassBeanDefinitionReader 加载源码
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {registrars.forEach((registrar, metadata) ->registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));//调用mybatis的钩子
}
第四步:扫描器何时被添加到 Spring
Spring Boot 阶段触发方法源码位置
SpringApplication.runrefresh()AbstractApplicationContext.refresh()容器启动
BeanDefinitionRegistryPostProcessorPostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()早于普通 Bean 创建
ImportBeanDefinitionRegistrar 回调AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions(...)立即执行

✅ 问题1:@Mapper 如何被注册到 Spring(钩子)

1.1 入口钩子:@Import(AutoConfiguredMapperScannerRegistrar.class)

Spring Boot 2.6+ 的 starter 里:

@Configuration(proxyBeanMethods = false)
@Import(AutoConfiguredMapperScannerRegistrar.class)   // 👈 关键钩子
@ConditionalOnMissingBean(MapperFactoryBean.class)
static class MapperScannerRegistrarNotFoundConfiguration {}
  • AutoConfiguredMapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar
    因此 Spring 会在 BeanDefinitionRegistryPostProcessor 阶段 回调:
public void registerBeanDefinitions(AnnotationMetadata meta, BeanDefinitionRegistry registry) {MybatisProperties props = beanFactory.getBean(MybatisProperties.class);ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);scanner.setAnnotationClass(Mapper.class);   // 只扫描带 @Mapper 的接口scanner.registerFilters();                  // 过滤条件scanner.doScan(StringUtils.toStringArray(props.getMapperLocations()));
}

1.2 扫描器:ClassPathMapperScanner#doScan

重写了父类 ClassPathBeanDefinitionScanner,核心在 processBeanDefinitions

@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> holders = super.doScan(basePackages); // 正常扫描for (BeanDefinitionHolder holder : holders) {GenericBeanDefinition bd = (GenericBeanDefinition) holder.getBeanDefinition();// 👇 狸猫换太子:把接口类名替换成 MapperFactoryBeanbd.setBeanClass(MapperFactoryBean.class);bd.getConstructorArgumentValues().addGenericArgumentValue(holder.getBeanDefinition().getBeanClassName());}return holders;
}

1.3 FactoryBean:MapperFactoryBean#getObject

  • FactoryBean 由 Spring 实例化并调用 getObject()
public T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);
}

返回的正是 MyBatis 生成的 JDK 动态代理,被注册成 单例 Bean,于是:

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;   // ← 注入的就是这个代理
}

✅ 问题2:@MapperScan 显式指定包的钩子

2.1 钩子:@Import(MapperScannerRegistrar.class)

@Retention(RetentionPolicy.RUNTIME)
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {String[] basePackages() default {};
}
  • MapperScannerRegistrar 同样实现 ImportBeanDefinitionRegistrar
    BeanDefinitionRegistryPostProcessor 阶段执行:
public void registerBeanDefinitions(AnnotationMetadata meta, BeanDefinitionRegistry registry) {AnnotationAttributes attrs = AnnotationAttributes.fromMap(meta.getAnnotationAttributes(MapperScan.class.getName()));if (attrs != null) {registerBeanDefinitions(meta, attrs, registry, ...);}
}

2.2 构造并注册 MapperScannerConfigurer

ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setBasePackage("com.xxx.mapper");   // 显式包路径
scanner.setAnnotationClass(null);           // 不限注解,扫描所有接口
scanner.registerFilters();
scanner.doScan(...);                        // 同 1.2 过程

✅ 为什么能拿到所有 Mapper 接口?

步骤关键类作用
过滤条件ClassPathMapperScanner重写 isCandidateComponent:允许 接口 通过
扫描范围用户指定包递归扫描 .class
狸猫换太子processBeanDefinitionsbeanClass 改成 MapperFactoryBean
代理生成MapperFactoryBean#getObjectsqlSession.getMapper(interface)

✅ 一句话总结

  • @MapperAutoConfiguredMapperScannerRegistrar → 扫描 @Mapper → 替换为 MapperFactoryBean → 代理注入。
  • @MapperScanMapperScannerRegistrar → 扫描指定包 → 同上链路。
  • 核心钩子ImportBeanDefinitionRegistrarBeanDefinitionRegistryPostProcessor 阶段完成 接口 → FactoryBean → 代理 Bean 的转换。
http://www.dtcms.com/a/329420.html

相关文章:

  • 后端Web实战-MySQL数据库
  • Si an(1)
  • Linux高级编程-framebuffer
  • 华为悦盒EC6108V9-1+4G版-盒子有【蓝色USB接口】的特殊刷机说明
  • 数据分析全景:从数据到决策的完整链路与核心要义
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • 【分布式 ID】一文详解美团 Leaf
  • TCP Socket 编程实战:实现简易英译汉服务
  • 函数扇入数(Fan-in)
  • NAT技术、代理服务器+网络通信各层协议
  • transforms的使用 小土堆pytorch记录
  • 深度学习流体力学:基于PyTorch的物理信息神经网络(PINN)完整实现
  • PyTorch Tensor完全指南:深度学习数据操作的核心艺术
  • C++编程学习(第21天)
  • LeetCode 分类刷题:1004. 最大连续1的个数 III
  • 【Linux】常用命令(三)
  • 智慧养老丨实用科普+避坑指南:科技如何让晚年生活更安全舒适?
  • 编译 C++ 程序时提示:fatal error: ‘json/json.h‘ file not found
  • 使用 HTML5 Canvas 打造炫酷的数字时钟动画
  • Linux基本操作命令
  • Go 语言函数详解:从基础到高阶的行为逻辑构建
  • 基于SD-WAN的医疗工厂弱电智能化机房方案:架构设计与实践
  • 具有熔断能力和活性探测的服务负载均衡解决方案
  • Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析
  • 【车联网kafka】Kafka核心架构与实战经验(第四篇)
  • docker镜像状态监控
  • 磁悬浮轴承转子设计避坑指南:深度解析核心要点与高可靠性策略
  • 【网络运维】Playbook进阶: 管理变量
  • 如何在 Spring Boot 中设计和返回树形结构的组织和部门信息
  • [AI React Web] E2B沙箱 | WebGPU | 组件树 | 智能重构 | 架构异味检测