Spring中Mybatis怎么扫描Mapper接口的?
package org.mybatis.spring.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {}
@MapperScan注解上标识@Import(MapperScannerRegistrar.class)
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
.
.
.
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
MapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar接口 spring启动时会执行registerBeanDefinitions注册MapperScannerConfigurer的BeanDefinition并将basePackage设置到propertyValues;
package org.mybatis.spring.mapper;
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
MapperConfigurer实现了BeanDefinitionRegistryPostProcessor, invokeBeanFactoryPostProcessors(beanFactory)时调用到
postProcessBeanDefinitionRegistry创建ClassPathMapperScanner 扫描器,scanner.scan()扫描Mapper。
Mybatis中@Mapper注解需要 MapperScannerConfigurer 中annotationClass设置为Mapper.class。
如果MapperScan定义basepackage 范围大于mapper范围并且没有指定要扫描的注解:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.net.immortal.user.service.command.AccountCmdService.login
在SpringBoot中不自定义MapperScan,则MybatisAutoConfiguration 自动配置AutoConfiguredMapperScannerRegistrar
basepackage为MainClass包,扫描Mapper注解。
最后
当@ConditionalOnMissingBean不满足条件时@Import也失效,原因:
推论@Condition不满足时@Import失效。