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.run → refresh() | AbstractApplicationContext.refresh() | 容器启动 |
BeanDefinitionRegistryPostProcessor | PostProcessorRegistrationDelegate.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 |
狸猫换太子 | processBeanDefinitions | 把 beanClass 改成 MapperFactoryBean |
代理生成 | MapperFactoryBean#getObject | sqlSession.getMapper(interface) |
✅ 一句话总结
- @Mapper →
AutoConfiguredMapperScannerRegistrar
→ 扫描@Mapper
→ 替换为MapperFactoryBean
→ 代理注入。 - @MapperScan →
MapperScannerRegistrar
→ 扫描指定包 → 同上链路。 - 核心钩子:
ImportBeanDefinitionRegistrar
在 BeanDefinitionRegistryPostProcessor 阶段完成 接口 → FactoryBean → 代理 Bean 的转换。