问题:mapper层和我们平时说的dao层指的是同一个内容,都是数据库操作的封装,但是在没有集成mybatis时,dao层的接口都是需要我们手动去写其实现类,可在springboot集成中我们却发现:我们并没有手动去实现Mapper接口,但工程却能实实在在的查询数据库,获取我们需要的数据,那么问题来了,mybatis是何时、何地、如何生成mapper代理实例的呢?
Mybatis的自动配置类:MybatisAutoConfiguration,MybatisAutoConfiguration会被当做配置类被spring解析,我们来看看spring容器会从此配置类中解析到什么
@ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
可以看到,MybatisAutoConfiguration创建了SqlSessionFactory实例(实际类型:DefaultSqlSessionFactory),并注册到了spring容器;此时我们应该还注意到方法实例化MapperScannerRegistrarNotFoundConfiguration类中@Import({ AutoConfiguredMapperScannerRegistrar.class }),这里
AutoConfiguredMapperScannerRegistrar继承了ImportBeanDefinitionRegistrar(关于该类可以看下这篇文章Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean),那么它的registerBeanDefinitions方法也会被调用
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
// 获取启动类所在的包,如:com.lee.shiro,会作为扫描开始的base package,一般只会有一个,但支持多个
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
scanner.setAnnotationClass(Mapper.class); // 设置扫谁,Mapper注解是被扫描对象
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages)); // 扫描所有mapper,进行bean定义处理
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
其中doScan方法会调用父类ClassPathBeanDefinitionScanner的doScan方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
// 找到包路径下的mapper
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);