@MapperScan的源码解析
时间: 2025-06-15 11:01:49 浏览: 28
<think>好的,用户想了解@MapperScan的源码解析。首先,我需要确认这个注解的上下文。@MapperScan是MyBatis框架中常用的注解,特别是在Spring集成中,比如MyBatis-Spring项目。它的主要作用是指定Mapper接口的扫描路径,让Spring容器能够自动注册这些接口为Bean。用户可能已经知道它的基本用法,但想深入理解其背后的实现机制。
接下来,我需要回忆MyBatis和Spring整合的基本原理。通常,Spring通过扫描组件来注册Bean,但Mapper接口是接口,没有实现类,所以常规的@ComponentScan无法处理。这时候@MapperScan就派上用场了,它利用了MyBatis的MapperScannerConfigurer或者通过自定义的扫描逻辑来生成代理类。
然后,我需要考虑用户可能的真实需求。用户可能是遇到了配置问题,或者想自定义扫描行为,了解源码可以帮助他们解决问题或进行扩展。也有可能用户在面试或学习中需要深入理解框架的工作原理。
接下来,我需要解析@MapperScan的源码结构。首先查看注解的定义,通常包括属性如basePackages、value、annotationClass等。然后看这个注解是如何被处理的,可能通过@Import引入了一个配置类,比如MapperScannerRegistrar,该类实现了ImportBeanDefinitionRegistrar接口,动态注册Bean定义。
然后,关键点在于MapperScannerConfigurer,这个类负责配置扫描器,设置基础包、注解过滤器等。扫描器会使用ClassPathMapperScanner,继承自Spring的ClassPathBeanDefinitionScanner,但重写了某些方法以适应MyBatis的特定需求,比如检查是否为接口,并生成对应的Bean定义,使用MapperFactoryBean来创建代理对象。
需要详细说明扫描过程,如何将接口转换为BeanDefinition,以及如何利用MyBatis的SqlSessionTemplate来创建代理。可能还需要提到如何处理多个数据源的情况,或者自定义注解的情况。
还要注意可能的异常情况,比如没有找到Mapper接口时的处理,或者重复扫描的问题。同时,可以提到@MapperScan和@Mapper的区别,前者是批量扫描,后者是单个声明。
最后,总结整个流程,从注解的导入到Bean定义的注册,再到代理对象的生成,确保用户能理解整个链路。用户可能需要了解如何自定义扫描器,或者解决扫描不到Mapper的问题,因此源码解析能帮助他们定位问题。</think>以下是对`@MapperScan`的源码解析(基于MyBatis-Spring 3.0+版本):
---
### 一、@MapperScan 核心作用
用于批量扫描MyBatis Mapper接口,通过`MapperScannerConfigurer`生成BeanDefinition并注册到Spring容器中,最终生成Mapper接口的代理对象。
---
### 二、源码执行流程
#### 1. 注解定义
```java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) // 关键点
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
// ...其他属性
}
```
#### 2. 核心类 MapperScannerRegistrar
实现`ImportBeanDefinitionRegistrar`接口,负责动态注册Bean:
```java
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(...) {
// 解析@MapperScan注解属性
AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(...);
// 创建扫描器配置Bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 设置扫描参数
builder.addPropertyValue("processPropertyPlaceHolders", true);
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.arrayToCommaDelimitedString(basePackages));
// 注册BeanDefinition
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
```
---
### 三、关键组件解析
#### 1. MapperScannerConfigurer
继承自`BeanDefinitionRegistryPostProcessor`,负责配置扫描器:
```java
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.registerFilters();
scanner.scan(StringUtils.toStringArray(this.basePackage));
}
}
```
#### 2. ClassPathMapperScanner
自定义扫描器(继承自Spring的`ClassPathBeanDefinitionScanner`):
```java
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类扫描
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 特殊处理:将接口转换为MapperFactoryBean
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class); // 关键替换
}
return beanDefinitions;
}
}
```
---
### 四、代理对象生成流程
1. **MapperFactoryBean**:继承自`SqlSessionDaoSupport`
2. **getObject()** 方法:
```java
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface); // 通过SqlSessionTemplate获取Mapper
}
```
3. **SqlSessionTemplate**:内部通过`MapperProxy`动态代理生成接口实现类
---
### 五、配置关键参数
通过`@MapperScan`可配置:
```java
@MapperScan(
basePackages = "com.example.mapper",
sqlSessionTemplateRef = "sqlSessionTemplate",
annotationClass = Repository.class,
markerInterface = BaseMapper.class
)
```
---
### 六、常见问题
1. **为什么Mapper接口不需要实现类?**
动态代理生成`MapperProxy`对象,通过方法名与XML/sql注解绑定
2. **多数据源如何配置?**
需配合`@Qualifier`指定`sqlSessionTemplateRef`
3. **扫描过滤机制**
默认扫描所有接口,可通过`annotationClass`/`markerInterface`过滤
---
通过以上源码分析,可以更清晰地定位`@MapperScan`相关的配置问题和扩展需求。
阅读全文
相关推荐




















