Mybatis源码解读-SpringBoot中配置加载和Mapper的生成
本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载。
建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此文章。
传送门:Mybatis源码解读-配置加载和Mapper的生成
问题
@MapperScan和@Mapper能一起用吗?
使用
创建工程不再赘述,参考demo
编写Mapper
Mapper的注册有两种方式:
- 在Mapper添加
@Mapper注解 - 在Application类添加
@MapperScan注解确定扫描包路径
后面会讲解这两种方式的区别
- 在Mapper添加
SqlSessionFactory和SqlSession
在讨论自动装配方式之前,先看看mybatis最简洁的demo
public static void main(String[] args) throws Exception {
// 配置文件路径
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1.读取配置,创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2.通过工厂获取SqlSession
SqlSession session = sqlSessionFactory.openSession();
try {
// 3.获取mapper代理对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
// 4.执行查询,此处才真正连接数据库
System.out.println(mapper.selectByName("张三"));
} finally {
// 5.关闭连接
session.close();
}
}
可以看到,首先需要创建SqlSessionFactory和SqlSession,在springboot中,这两者通过自动装配完成。
在mybatis-spring-boot-autoconfigure-x.x.x.jar的spring.factories中,可以看到自动装配注入了MybatisAutoConfiguration类
public class MybatisAutoConfiguration implements InitializingBean {
......
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
......
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}
SqlSessionTemplate是SqlSession的子类,所以现在二者都有了。
Mapper
Mapper的扫描分两种方式讨论
@MapperScan方式
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan { }
可以看到,导入了MapperScannerRegistrar类
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(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
} void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
......
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
因为MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,所以会被调用registerBeanDefinitions方法,最后注册MapperScannerConfigurer
咱们先记住MapperScannerConfigurer这个类,去看看@Mapper的方式
@Mapper方式
在
MybatisAutoConfiguration中,有这么一段代码@org.springframework.context.annotation.Configuration
// 如果满足条件,则导入AutoConfiguredMapperScannerRegistrar
@Import(AutoConfiguredMapperScannerRegistrar.class)
// 如果MapperFactoryBean和MapperScannerConfigurer都没注册,则满足条件
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { ...... }
我们在@MapperScan方式看到,是已经注册了MapperScannerConfigurer类的。所以,@MapperScan会覆盖@Mapper
继续看看
AutoConfiguredMapperScannerRegistrarpublic static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ......
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("annotationClass", Mapper.class);
...... registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
} ......
}
可以看到,同样是注册了MapperScannerConfigurer
也就是两种注解方式都是通过MapperScannerConfigurer扫描mapper注册的
通用部分
继续追踪MapperScannerConfigurer的调用链
// MapperScannerConfigurer#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
......
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
......
// 注册过滤器(@Mapper和@MapperScan的区别体现在这里)
scanner.registerFilters();
// 开始扫描bean
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
} public void registerFilters() {
boolean acceptAllInterfaces = true; // 如果指定了扫描类型(@Mapper走这里)
// annotationClass在前面的AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions被注入
// 就是这段builder.addPropertyValue("annotationClass", Mapper.class);
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
} ......
// 如果没指定扫描类型,则扫描全部(@MapperScan走这里)
if (acceptAllInterfaces) {
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
} // exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
看完了过滤器的注册,继续回到扫描逻辑scanner.scan
// ClassPathMapperScanner#scan(String... basePackages) -->
// ClassPathMapperScanner#doScan(String... basePackages)
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 扫描mapper(此时是原始对象)
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// 通过MapperFactoryBean类将mapper对象转换成代理对象MapperProxy
processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
}
答案
@MapperScan和@Mapper能一起用(不会报错),但是@Mapper是没有效果的。
Mybatis源码解读-SpringBoot中配置加载和Mapper的生成的更多相关文章
- Mybatis源码解读-配置加载和Mapper的生成
问题 Mybatis四大对象的创建顺序? Mybatis插件的执行顺序? 工程创建 环境:Mybatis(3.5.9) mybatis-demo,参考官方文档 简单示例 这里只放出main方法的示例, ...
- Mybatis 源码分析--Configuration.xml配置文件加载到内存
(补充知识点: 1 byte(字节)=8 bit(位) 通常一个标准英文字母占一个字节位置,一个标准汉字占两个字节位置:字符的例子有:字母.数字系统或标点符号) 1.创建SqlSessionFacto ...
- Mybatis源码解读-插件
插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...
- spring IOC DI AOP MVC 事务, mybatis 源码解读
demo https://gitee.com/easybao/aop.git spring DI运行时序 AbstractApplicationContext类的 refresh()方法 1: pre ...
- MyBatis源码解读之延迟加载
1. 目的 本文主要解读MyBatis 延迟加载实现原理 2. 延迟加载如何使用 Setting 参数配置 设置参数 描述 有效值 默认值 lazyLoadingEnabled 延迟加载的全局开关.当 ...
- MyBatis源码解读(3)——MapperMethod
在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在 ...
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)
doCreateBean方法 上文[Spring源码分析]非懒加载的单例Bean初始化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下 ...
- 【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作
前言 之前两篇文章[Spring源码分析]非懒加载的单例Bean初始化过程(上篇)和[Spring源码分析]非懒加载的单例Bean初始化过程(下篇)比较详细地分析了非懒加载的单例Bean的初始化过程, ...
随机推荐
- openstack命令创建云主机实例
@ 目录 前言 上传centos镜像 创建实例 创建外网卡 修改安全组规则 创建云主机实例 前言 简单创建云主机实例只需要上传一个测试镜像.创建一张外网卡.创建一个实例类型.修改安全组规则即可 注:这 ...
- Jmeter接口测试流程详解(中科软测认证中心)
1.jmeter简介 Jmeter是由Apache公司开发的java开源项目,所以想要使用它必须基于java环境才可以: Jmeter采用多线程,允许通过多个线程并发取样或通过独立的线程对不同的功能同 ...
- 690. Employee Importance - LeetCode
Question 690. Employee Importance Example 1: Input: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1 Outp ...
- 好客租房15-jsx中的条件渲染
jsx中的条件渲染 场景:loding效果 条件渲染:根据条件渲染特定的jsx结构 可以使用if/else或者三元运算符和逻辑和运算符实现 //导入react import React from &q ...
- vue2 使用 swiper 轮播图效果
第一步.先安装swiper插件 npm install swiper@3.4.1 --save-dev 第二步.组件内引入swiper插件 import Swiper from 'swiper' im ...
- [codeforces] 暑期训练之打卡题(一)
每个标题都做了题目原网址的超链接 Day1<Vanya and Lanterns> 题意: 一条长度为 l 的街道,在这条街道上放置了n个相同的灯,街道一端位置记为0,每个灯的位置在ai处 ...
- 基于Proxmox平台搭建3D云教室
背景 本文介绍了在 Proxmox VE 虚拟化平台上使用NVIDIA A16 GPU,开启vGPU特性,利用DoraCloud 搭建3D云教室的方案. Proxmox virtualization ...
- 使用ROOT用户运行Jenkins
!本教程仅适用于以YUM.APT等包管理器安装的Jenkins. 使用ROOT用户运行Jenkins,以保证Jenkins以最高权限执行任务. 注:会存在安全风险! 1.找到Jenkins启动脚本 通 ...
- python发QQ邮件
python发qq邮件相对比较简单,网上教程一大把:固定套路,后面封装看自己怎么方便可以怎样进行封装:原版代码如下: """ # -*- coding : utf-8 - ...
- SpringCloudAlibaba分布式流量控制组件Sentinel实战与源码分析(上)
概述 定义 Sentinel官网地址 https://sentinelguard.io/zh-cn/index.html 最新版本v1.8.4 Sentinel官网文档地址 https://senti ...