getObjectType() {
return this.mapperInterface;
}
```
## 3.5 isSingleton
```
// MapperFactoryBean.java
@Override
public boolean isSingleton() {
return true;
}
```
# 4. @MapperScan
> 艿艿:本小节,需要胖友对 Spring IOC 有一定的了解。如果不熟悉的胖友,建议不需要特别深入的理解。或者说,先去看 [《精尽 Spring 源码解析》](http://svip.iocoder.cn/categories/Spring/) 。
`org.mybatis.spring.annotation.@MapperScan` 注解,指定需要扫描的包,将包中符合的 Mapper 接口,注册成 `beanClass` 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象。代码如下:
```
// MapperScan.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise
* annotation declarations e.g.:
* {@code @MapperScan("org.my.pkg")} instead of {@code @MapperScan(basePackages = "org.my.pkg"})}.
*
* 和 {@link #basePackages()} 相同意思
*
* @return base package names
*/
String[] value() default {};
/**
* Base packages to scan for MyBatis interfaces. Note that only interfaces
* with at least one method will be registered; concrete classes will be
* ignored.
*
* 扫描的包地址
*
* @return base package names for scanning mapper interface
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*
* @return classes that indicate base package for scanning mapper interface
*/
Class>[] basePackageClasses() default {};
/**
* The {@link BeanNameGenerator} class to be used for naming detected components
* within the Spring container.
*
* @return the class of {@link BeanNameGenerator}
*/
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* This property specifies the annotation that the scanner will search for.
*
* The scanner will register all interfaces in the base package that also have
* the specified annotation.
*
* Note this can be combined with markerInterface.
*
* 指定注解
*
* @return the annotation that the scanner will search for
*/
Class extends Annotation> annotationClass() default Annotation.class;
/**
* This property specifies the parent that the scanner will search for.
*
* The scanner will register all interfaces in the base package that also have
* the specified interface class as a parent.
*
* Note this can be combined with annotationClass.
*
* 指定接口
*
* @return the parent that the scanner will search for
*/
Class> markerInterface() default Class.class;
/**
* Specifies which {@code SqlSessionTemplate} to use in the case that there is
* more than one in the spring context. Usually this is only needed when you
* have more than one datasource.
*
* 指向的 SqlSessionTemplate 的名字
*
* @return the bean name of {@code SqlSessionTemplate}
*/
String sqlSessionTemplateRef() default "";
/**
* Specifies which {@code SqlSessionFactory} to use in the case that there is
* more than one in the spring context. Usually this is only needed when you
* have more than one datasource.
*
* 指向的 SqlSessionFactory 的名字
*
* @return the bean name of {@code SqlSessionFactory}
*/
String sqlSessionFactoryRef() default "";
/**
* Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
*
* 可自定义 MapperFactoryBean 的实现类
*
* @return the class of {@code MapperFactoryBean}
*/
Class extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
```
- 属性比较多,实际上,胖友常用的就是 `value()` 属性。
- 重点是 `@Import(MapperScannerRegistrar.class)` 。为什么呢?`@Import` 注解,负责资源的导入。如果导入的是一个 Java 类,例如此处为 MapperScannerRegistrar 类,Spring 会将其注册成一个 Bean 对象。而 MapperScannerRegistrar 类呢?详细解析,见 [「4.1 MapperScannerRegistrar」](https://svip.iocoder.cn/MyBatis/Spring-Integration-2/#) 。
使用示例如下:
```
@Configuration
@ImportResource("classpath:org/mybatis/spring/sample/config/applicationContext-infrastructure.xml")
@MapperScan("org.mybatis.spring.sample.mapper") // here
static class AppConfig {
}
```
- 该示例来自 `org.mybatis.spring.sample.SampleEnableTest` 单元测试。胖友可以基于它调试。
## 4.1 MapperScannerRegistrar
`org.mybatis.spring.annotation.MapperScannerRegistrar` ,实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口,`@MapperScann` 的注册器,负责将扫描到的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象。
### 4.1.1 构造方法
```
// MapperScannerRegistrar.java
/**
* ResourceLoader 对象
*/
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
```
- 因为实现了 ResourceLoaderAware 接口,所以 `resourceLoader` 属性,能够被注入。
### 4.1.2 registerBeanDefinitions
```
// MapperScannerRegistrar.java
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// <1> 获得 @MapperScan 注解信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// <2> 扫描包,将扫描到的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象
registerBeanDefinitions(mapperScanAttrs, registry);
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
// <3.1> 创建 ClassPathMapperScanner 对象
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// <3.2> 设置 resourceLoader 属性到 scanner 中
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
// <3.3> 获得 @(MapperScan 注解上的属性,设置到 scanner 中
Class extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
// <4> 获得要扫描的包
List basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")) // 包
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("basePackages")) // 包
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getClassArray("basePackageClasses")) // 类
.map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
// <5> 注册 scanner 的过滤器
scanner.registerFilters();
// <6> 执行扫描
scanner.doScan(StringUtils.toStringArray(basePackages));
}
```
- `<1>` 处,获得 `@MapperScan` 注解信息。
- `<2>` 处,调用 `#registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry)` 方法,扫描包,将扫描到的 Mapper 接口,注册成 `beanClass` 为 MapperFactoryBean 的 BeanDefinition 对象。
- ```
<3.1>
```
处,创建 ClassPathMapperScanner 对象。下面的代码,都是和 ClassPathMapperScanner 相关。所以,详细的解析,最终看
「4.2 ClassPathMapperScanner」
。
- `<3.2>` 处, 设置 `resourceLoader` 属性到 `scanner` 中。
- `<3.3>` 处,获得 `@MapperScan` 注解上的属性,设置到 `scanner` 中。
- `<4>` 处,获得要扫描的包。
- `<5>` 处,调用 `ClassPathMapperScanner#registerFilters()` 方法,注册 scanner 的过滤器。
- `<6>` 处,调用 `ClassPathMapperScanner#doScan(String... basePackages)` 方法,执行扫描,将扫描到的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象。
- 😈 上面看了一堆的 ClassPathMapperScanner 类的调用,下面开始我们的旅程。
## 4.2 ClassPathMapperScanner
`org.mybatis.spring.mapper.ClassPathMapperScanner` ,继承 `org.springframework.context.annotation.ClassPathMapperScanner` 类,负责执行扫描,将扫描到的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象。
可能很多胖友不熟悉 ClassPathMapperScanner 类,可以看看 [《Spring自定义类扫描器》](https://fangjian0423.github.io/2017/06/11/spring-custom-component-provider/) 文章。哈哈哈,艿艿也是现学的。
### 4.2.1 构造方法
```
// ClassPathMapperScanner.java
/**
* 是否添加到 {@link org.apache.ibatis.session.Configuration} 中
*/
private boolean addToConfig = true;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
/**
* {@link #sqlSessionTemplate} 的 bean 名字
*/
private String sqlSessionTemplateBeanName;
/**
* {@link #sqlSessionFactory} 的 bean 名字
*/
private String sqlSessionFactoryBeanName;
/**
* 指定注解
*/
private Class extends Annotation> annotationClass;
/**
* 指定接口
*/
private Class> markerInterface;
/**
* MapperFactoryBean 对象
*/
private MapperFactoryBean> mapperFactoryBean = new MapperFactoryBean<>();
// ... 省略 setting 方法
```
### 4.2.2 registerFilters
`#registerFilters()` 方法,注册过滤器。代码如下:
```
// ClassPathMapperScanner.java
/**
* Configures parent scanner to search for the right interfaces. It can search
* for all interfaces or just for those that extends a markerInterface or/and
* those annotated with the annotationClass
*
* 注册过滤器
*/
public void registerFilters() {
boolean acceptAllInterfaces = true; // 是否接受所有接口
// if specified, use the given annotation and / or marker interface
// 如果指定了注解,则添加 INCLUDE 过滤器 AnnotationTypeFilter 对象
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false; // 标记不是接受所有接口
}
// override AssignableTypeFilter to ignore matches on the actual marker interface
// 如果指定了接口,则添加 INCLUDE 过滤器 AssignableTypeFilter 对象
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false; // 标记不是接受所有接口
}
// 如果接受所有接口,则添加自定义 INCLUDE 过滤器 TypeFilter ,全部返回 true
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
// 添加 INCLUDE 过滤器,排除 package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
```
- 根据配置,添加 INCLUDE 和 EXCLUDE 过滤器。
### 4.2.3 doScan
`#doScan(String... basePackages)` 方法,执行扫描,将扫描到的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象。代码如下:
```
// ClassPathMapperScanner.java
/**
* Calls the parent search that will search and register all the candidates.
* Then the registered objects are post processed to set them as
* MapperFactoryBeans
*/
@Override
public Set doScan(String... basePackages) {
// <1> 执行扫描,获得包下符合的类们,并分装成 BeanDefinitionHolder 对象的集合
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
// 处理 BeanDefinitionHolder 对象的集合
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
```
- `<1>` 处,调用父 `ClassPathBeanDefinitionScanner#doScan(basePackages)` 方法,执行扫描,获得包下符合的类们,并分装成 BeanDefinitionHolder 对象的集合。
- `<2>` 处,调用 `#processBeanDefinitions((Set beanDefinitions)` 方法,处理 BeanDefinitionHolder 对象的集合。代码如下:
```
// ClassPathMapperScanner.java
private void processBeanDefinitions(Set beanDefinitions) {
GenericBeanDefinition definition;
// <1> 遍历 BeanDefinitionHolder 数组,逐一设置属性
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + beanClassName + "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
// <2> 此处 definition 的 beanClass 为 Mapper 接口,需要修改成 MapperFactoryBean 类,从而创建 Mapper 代理对象
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
// <3> 设置 `MapperFactoryBean.addToConfig` 属性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false; // <4.1>是否已经显式设置了 sqlSessionFactory 或 sqlSessionFactory 属性
// <4.2> 如果 sqlSessionFactoryBeanName 或 sqlSessionFactory 非空,设置到 `MapperFactoryBean.sqlSessionFactory` 属性
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;
}
// <4.3> 如果 sqlSessionTemplateBeanName 或 sqlSessionTemplate 非空,设置到 `MapperFactoryBean.sqlSessionTemplate` 属性
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;
}
// <4.4> 如果未显式设置,则设置根据类型自动注入
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
```
- 虽然方法很长,重点就是修改 BeanDefinitionHolder 的相关属性。
- `<1>` 处,遍历 BeanDefinitionHolder 数组,逐一设置属性。
- `<2>` 处,此处 `definition` 的 `beanClass` 为 Mapper 接口,需要修改成 MapperFactoryBean 类,从而创建 Mapper 代理对象。
- `<3>` 处,设置 `MapperFactoryBean.addToConfig` 属性。
- ```
<4.1>
```
处,是否已经显式设置了
```
sqlSessionFactory 或 sqlSessionFactory
```
属性。
- `<4.2>` 处,如果 `sqlSessionFactoryBeanName` 或 `sqlSessionFactory` 非空,设置到 `MapperFactoryBean.sqlSessionFactory` 属性。
- `<4.3>` 处,如果 `sqlSessionTemplateBeanName` 或 `sqlSessionTemplate` 非空,设置到 `MapperFactoryBean.sqlSessionTemplate` 属性。
- `<4.4>` 处,如果未显式设置,**则设置根据类型自动注入**。
### 4.3 @MapperScans
`org.mybatis.spring.annotation.@MapperScans` ,多 `@MapperScan` 的注解,功能是相同的。代码如下:
```
// MapperScans.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.RepeatingRegistrar.class)
public @interface MapperScans {
/**
* @return @MapperScan 数组
*/
MapperScan[] value();
}
```
- 此处,`@Import(MapperScannerRegistrar.RepeatingRegistrar.class)` 是 RepeatingRegistrar 类。
### 4.4 RepeatingRegistrar
RepeatingRegistrar ,是 MapperScannerRegistrar 的内部静态类,继承 MapperScannerRegistrar 类,`@MapperScans` 的注册器。代码如下:
```
// MapperScannerRegistrar.java
static class RepeatingRegistrar extends MapperScannerRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获得 @MapperScans 注解信息
AnnotationAttributes mapperScansAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
// 遍历 @MapperScans 的值,调用 `#registerBeanDefinitions(mapperScanAttrs, registry)` 方法,循环扫描处理
Arrays.stream(mapperScansAttrs.getAnnotationArray("value"))
.forEach(mapperScanAttrs -> registerBeanDefinitions(mapperScanAttrs, registry));
}
}
```
- [「4.1.2 registerBeanDefinitions」](https://svip.iocoder.cn/MyBatis/Spring-Integration-2/#) 的**循环**版。
# 5. 自定义 `` 标签
使用示例如下:
```
```
- 该示例来自 `org.mybatis.spring.sample.SampleNamespaceTest` 单元测试。胖友可以基于它调试。
简单来理解,`` 标签,和 `@MapperScan` 注解,用途是等价的。
## 5.1 spring.schemas
MyBatis 在 `META-INF/spring.schemas` 定义如下:
```
http\://mybatis.org/schema/mybatis-spring-1.2.xsd=org/mybatis/spring/config/mybatis-spring.xsd
http\://mybatis.org/schema/mybatis-spring.xsd=org/mybatis/spring/config/mybatis-spring.xsd
```
- xmlns 为 `http://mybatis.org/schema/mybatis-spring-1.2.xsd` 或 `http://mybatis.org/schema/mybatis-spring.xsd` 。
- xsd 为 `org/mybatis/spring/config/mybatis-spring.xsd` 。
## 5.2 mybatis-spring.xsd
[](https://github.com/YunaiV/mybatis-spring/blob/master/src/main/java/org/mybatis/spring/config/mybatis-spring.xsd)mybatis-spring.xsd定义如下:[mybatis-spring.xsd](http://static.iocoder.cn/images/MyBatis/2020_06_04/01.png)
## 5.3 spring.handler
`spring.handlers` 定义如下:
```
http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
```
定义了 MyBatis 的 XML Namespace 的处理器 NamespaceHandler 。
## 5.4 NamespaceHandler
`org.mybatis.spring.config.NamespaceHandler` ,继承 NamespaceHandlerSupport 抽象类,MyBatis 的 XML Namespace 的处理器。代码如下:
```
// NamespaceHandler.java
public class NamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());
}
}
```
- `` 标签,使用 MapperScannerBeanDefinitionParser 解析。
## 5.5 MapperScannerBeanDefinitionParser
`org.mybatis.spring.config.MapperScannerBeanDefinitionParser` ,实现 BeanDefinitionParser 接口,`` 的解析器。代码如下:
```
// BeanDefinitionParser.java
public class MapperScannerBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATTRIBUTE_BASE_PACKAGE = "base-package";
private static final String ATTRIBUTE_ANNOTATION = "annotation";
private static final String ATTRIBUTE_MARKER_INTERFACE = "marker-interface";
private static final String ATTRIBUTE_NAME_GENERATOR = "name-generator";
private static final String ATTRIBUTE_TEMPLATE_REF = "template-ref";
private static final String ATTRIBUTE_FACTORY_REF = "factory-ref";
/**
* {@inheritDoc}
*/
@Override
public synchronized BeanDefinition parse(Element element, ParserContext parserContext) {
// 创建 ClassPathMapperScanner 对象
ClassPathMapperScanner scanner = new ClassPathMapperScanner(parserContext.getRegistry());
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
XmlReaderContext readerContext = parserContext.getReaderContext();
scanner.setResourceLoader(readerContext.getResourceLoader()); // 设置 resourceLoader 属性
try {
// 解析 annotation 属性
String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);
if (StringUtils.hasText(annotationClassName)) {
@SuppressWarnings("unchecked")
Class extends Annotation> markerInterface = (Class extends Annotation>) classLoader.loadClass(annotationClassName);
scanner.setAnnotationClass(markerInterface);
}
// 解析 marker-interface 属性
String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);
if (StringUtils.hasText(markerInterfaceClassName)) {
Class> markerInterface = classLoader.loadClass(markerInterfaceClassName);
scanner.setMarkerInterface(markerInterface);
}
// 解析 name-generator 属性
String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);
if (StringUtils.hasText(nameGeneratorClassName)) {
Class> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);
scanner.setBeanNameGenerator(nameGenerator);
}
} catch (Exception ex) {
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
// 解析 template-ref 属性
String sqlSessionTemplateBeanName = element.getAttribute(ATTRIBUTE_TEMPLATE_REF);
scanner.setSqlSessionTemplateBeanName(sqlSessionTemplateBeanName);
// 解析 factory-ref 属性
String sqlSessionFactoryBeanName = element.getAttribute(ATTRIBUTE_FACTORY_REF);
scanner.setSqlSessionFactoryBeanName(sqlSessionFactoryBeanName);
// 注册 scanner 的过滤器
scanner.registerFilters();
// 获得要扫描的包
String basePackage = element.getAttribute(ATTRIBUTE_BASE_PACKAGE);
// 执行扫描
scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
return null;
}
}
```
- 代码实现上,和 [「4.2 ClassPathMapperScanner」](https://svip.iocoder.cn/MyBatis/Spring-Integration-2/#) 是基本一致的。所以就不详细解析啦。
# 6. MapperScannerConfigurer
`org.mybatis.spring.mapper.MapperScannerConfigurer` ,实现 BeanDefinitionRegistryPostProcessor、InitializingBean、ApplicationContextAware、BeanNameAware 接口,定义需要扫描的包,将包中符合的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象。
使用示例如下:
```
// XML
```
- 该示例来自 `org.mybatis.spring.sample.MapperScannerConfigurer` 单元测试。胖友可以基于它调试。
## 6.1 构造方法
```
// MapperScannerConfigurer.java
private String basePackage;
private boolean addToConfig = true;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
private Class extends Annotation> annotationClass;
private Class> markerInterface;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
// 省略 setting 方法
```
## 6.2 afterPropertiesSet
```
// MapperScannerConfigurer.java
@Override
public void afterPropertiesSet() throws Exception {
notNull(this.basePackage, "Property 'basePackage' is required");
}
```
- 啥都不做。
## 6.3 postProcessBeanFactory
```
// MapperScannerConfigurer.java
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// left intentionally blank
}
```
## 6.4 postProcessBeanDefinitionRegistry
```
// MapperScannerConfigurer.java
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// <1> 如果有属性占位符,则进行获得,例如 ${basePackage} 等等
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// <2> 创建 ClassPathMapperScanner 对象,并设置其相关属性
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 过滤器
scanner.registerFilters();
// 执行扫描
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
```
- `<1>` 处,调用 `#processPropertyPlaceHolders()` 方法,如果有属性占位符,则进行获得,例如 `${basePackage}` 等等。代码如下:
```
// MapperScannerConfigurer.java
private void processPropertyPlaceHolders() {
Map prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext)
.getBeanFactory().getBeanDefinition(beanName);
// PropertyResourceConfigurer does not expose any methods to explicitly perform
// property placeholder substitution. Instead, create a BeanFactory that just
// contains this mapper scanner and post process the factory.
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition(beanName, mapperScannerBean);
for (PropertyResourceConfigurer prc : prcs.values()) {
prc.postProcessBeanFactory(factory);
}
PropertyValues values = mapperScannerBean.getPropertyValues();
this.basePackage = updatePropertyValue("basePackage", values);
this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
}
}
// 获得属性值,并转换成 String 类型
private String updatePropertyValue(String propertyName, PropertyValues values) {
PropertyValue property = values.getPropertyValue(propertyName);
Object value = property.getValue();
if (value instanceof String) {
return value.toString();
} else if (value instanceof TypedStringValue) {
return ((TypedStringValue) value).getValue();
} else {
return null;
}
}
```
- `<2>` 处,代码实现上,和 [「4.2 ClassPathMapperScanner」](https://svip.iocoder.cn/MyBatis/Spring-Integration-2/#) 是基本一致的。所以就不详细解析啦。
# 666. 彩蛋
略微冗长,但是易于理解的一篇文章。
我们简单把 3 ~ 6 小节做个小结的话:
- 「3」MapperFactoryBean 类,是最**基础**的、**单个**的负责创建 Mapper 代理对象的类。
- 「4」「5」「6」,都是基于 MapperFactoryBean 之上,使用 ClassPathMapperScanner 扫描指定包,创建成 MapperFactoryBean 对象,从而创建 Mapper 代理对象。