springboot集成下,mybatis的mapper代理对象究竟是如何生成的
前言
开心一刻
中韩两学生辩论。
中:端午节是属于谁的?
韩:韩国人!
中:汉字是谁发明的?
韩:韩国人!
中:中医是属于谁的?
韩:韩国人!
中:那中国人到底发明过什么?
韩:韩国人!

前情回顾
Mybatis源码解析 - mapper代理对象的生成,你有想过吗,我们讲到了mybatis操作数据库的流程:先创建SqlSessionFactory,然后创建SqlSession,然后再创建获取mapper代理对象,最后利用mapper代理对象完成数据库的操作;Mapper代理对象的创建,利用的是JDK的动态代理,InvocationHandler是MapperProxy,后续Mapper代理对象方法的执行都会先经过MapperProxy的invoke方法。
但是,此时SqlSessionFactory的创建、SqlSession的创建以及mapper代理对象的获取都是我们手动操作的,实际应用中,mybatis往往也不会单独使用,绝大多数都是集成在spring中,也就是说我们无需手动去管理mybatis相关对象的生命周期,全部都由spring容器统一管理,那么spring是什么时候在哪创建的mybatis的相关对象的呢?尤其是mapper代理对象MapperProxy的创建
Springboot集成mybatis
当springboot(其实还是spring)集成mybatis后,mybatis的对象是交给spring容器管理的,只会实例化一次,然后伴随着spring容器一直存在,直到spring容器销毁
自动配置:MybatisAutoConfiguration

Mybatis的自动配置类:MybatisAutoConfiguration,至于如何加载此类,可参考:spring-boot-2.0.3启动源码篇一 - SpringApplication构造方法
MybatisAutoConfiguration会被当做配置类被spring解析,我们来看看spring容器会从此配置类中解析到什么

创建了SqlSessionFactory实例(实际类型:DefaultSqlSessionFactory),并注册到了spring容器;此时我们应该还注意到
@Import({ AutoConfiguredMapperScannerRegistrar.class })
AutoConfiguredMapperScannerRegistrar继承了ImportBeanDefinitionRegistrar(注意看类注释,有兴许的可以更深入的研究下),那么它的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);
}
}

以我们启动类所在的包(com.lee.shiro)为基包,扫描所有的mapper,然后修改所有mapper在spring容器中的bean定义,将mapper的beanClass全部指向了MapperFactoryBean
mapper代理对象的创建:MapperFactoryBean
MapperFactoryBean继承SqlSessionDaoSupport,SqlSessionDaoSupport有两个方法用来设置SqlSession
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
可以看到SqlSession的实际类型是:SqlSessionTemplate,SqlSessionTemplate在MybatisAutoConfiguration以@Bean方式创建的
Spring在创建Service实例:UserServiceImpl的时候,发现依赖mapper(可能还有其他的实例依赖mapper),那么就会去spring容器获取mapper实例,没有则进行创建,然后注入进来(依赖注入);具体创建过程如下
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建mapper对象,beanName:com.lee.shiro.mapper.UserMapper,创建出来的实例实际上是MapperFactoryBean类型
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
// 获取给定bean实例的对象,如果是FactoryBean,则获取bean实例本身或其创建的对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

因为Spring在mapper扫描的时候,将所有mapper bean定义中的beanClass设置成了MapperFactoryBean(继承了FactoryBean),所以通过createBean方法创建的mapper实例实际上是MapperFactoryBean对象,然后通过
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory.
// isFactoryDereference方法判断name中是否有&字符
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
} // Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
// 此时的beanInstance可能是一个普通bean,也可能是一个FactoryBean
// 如果是一个FactoryBean,那么就用它创建想要的bean实例
// 此if表示,如果beanInstance是普通bean,或者本来就想要FactoryBean实例,则直接返回beanInstance
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
} Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
// 此时表明beanInstance是一个FactoryBean,并且不是想要FactoryBean实例
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 通过FactoryBean实例创建我们想要的实例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
获取真正想要的bean实例,如果beanInstance是普通bean,或者本来就想要FactoryBean实例(beanName中有&),那么直接返回beanInstance,否则用FactoryBean实例来创建我们想要的实例对象。说回来就是会调用MapperFactoryBean的getObject()方法来获取Mapper的代理对象
后续流程就可以参考:Mybatis源码解析 - mapper代理对象的生成,你有想过吗
至此,前情回顾中的问题也就清晰了
总结
1、自动配置的过程中,spring会扫描所有的mapper,并将所有mapper bean定义中的beanClass指向MapperFactoryBean;
2、创建mapper实例的时候,根据bean定义创建的实例实际上是MapperFactoryBean实例,然后再利用MapperFactoryBean获取mapper实例(调用MapperFactoryBean的getObject方法,mybatis会利用jdk的动态代理创建mapper代理对象);
3、对比Mybatis源码解析 - mapper代理对象的生成,你有想过吗,其实就是将我们手动创建的过程通过自动配置,将创建过程交给了spring;
4、关于spring的FactoryBean,请看看这篇:Spring拓展接口之FactoryBean,我们来看看其源码实现
springboot集成下,mybatis的mapper代理对象究竟是如何生成的的更多相关文章
- Mybatis源码解析 - mapper代理对象的生成,你有想过吗
前言 开心一刻 本人幼教老师,冬天戴帽子进教室,被小朋友看到,这时候,有个小家伙对我说:老师你的帽子太丑,赶紧摘了吧.我逗他:那你好好学习,以后给老师买个漂亮的?这孩子想都没想立刻回答:等我赚钱了,带 ...
- springboot学习笔记:11.springboot+shiro+mysql+mybatis(通用mapper)+freemarker+ztree+layui实现通用的java后台管理系统(权限管理+用户管理+菜单管理)
一.前言 经过前10篇文章,我们已经可以快速搭建一个springboot的web项目: 今天,我们在上一节基础上继续集成shiro框架,实现一个可以通用的后台管理系统:包括用户管理,角色管理,菜单管理 ...
- Mybatis的mapper代理开发方法
一.开发规范 1.映射文件中的namespase等于mapper接口类路径 2.statement的id与mapper中的方法名一致 3.让mapper的接口方法输入参数类型与statement中的p ...
- mybatis——使用mapper代理开发方式
---------------------------------------------------------------generatorConfig.xml------------------ ...
- Mybatis的mapper代理开发dao方法
看完了之前的mybatis原始的dao开发方法是不是觉得有点笨重,甚至说没有发挥mybatis 作为一个框架的优势.总结了一下,原始的dao方法有以下几点不足之处 dao接口实现方法中存在大量的模板方 ...
- Mybatis非mapper代理配置
转: Mybatis非mapper代理配置 2017年04月26日 20:13:48 待长的小蘑菇 阅读数:870 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog. ...
- mybatis关于ORM的使用以及设计(二)[DaoInterface 转换 Mapper代理对象]
第一节中,分析了Mybatis的ORM框架的初始化,这篇来分析SQL执行过程中,对象->SQL是如何转换的 其中包含两种映射思想 ①DAO接口->Mapper实例 ②执行DAO的方法时,参 ...
- Spring+SpringMVC+Mybatis大整合(SpringMVC采用REST风格、mybatis采用Mapper代理)
整体目录结构: 其中包下全部是采用mybatis自动生成工具生成. mybatis自动生成文件 <?xml version="1.0" encoding="UTF- ...
- springboot学习笔记:8. springboot+druid+mysql+mybatis+通用mapper+pagehelper+mybatis-generator+freemarker+layui
前言: 开发环境:IDEA+jdk1.8+windows10 目标:使用springboot整合druid数据源+mysql+mybatis+通用mapper插件+pagehelper插件+mybat ...
随机推荐
- Rest API 操作List Items
获取所有的List Itemsfunction getItems(url) { $.ajax({ url: url, type: "GET", headers: { "a ...
- 使用POST下载文件
一直以来,JS都没有比较好的可以直接处理二进制的方法.而Blob的存在,允许我们可以通过JS直接操作二进制数据.一.下载util.fetchDownload= function (opt,data) ...
- React(八)样式及CSS模块化
(1)内联样式 注:样式要采用驼峰命令发,如果非要使用原生css样式写法,需加引号 缺点:一些动画,伪类不能使用 class App extends Component { constructor(p ...
- manacher算法,求回文串
用来求字符串最长回文串或者回文串的总数量 #include<map> #include<queue> #include<stack> #include<cma ...
- 发布版本Debug和Release的区别
1.Debug是调试版本不会对项目发布的内容进行优化,并且包含调试信息容量会大很多 2.Release不对源代码进行调试,对应用程序的速度进行优化,使得发布的内容容量等都是最优的
- 2019_BUAAOO_第一单元总结
前言 OO第一单元共有三次作业,分别为多项式求导.带有三角函数与幂函数的表达式求导.带有嵌套表达式因子的表达式求导.虽然这三次作业都离不开求导,可是每次作业的复杂度都是大大递增的.对于习惯于面向过程编 ...
- Spring源码学习相关记录
Spring单例实现: protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object sin ...
- spring的配置和使用
spring的配置和使用 1. 创建基于java的配置. 配置极少量的XML来启用java配置: <?xml version="1.0" encoding="U ...
- TCP协议学习总结(上)
在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...
- Spring Cloud 微服务架构全链路实践
阅读目录: 1. 网关请求流程 2. Eureka 服务治理 3. Config 配置中心 4. Hystrix 监控 5. 服务调用链路 6. ELK 日志链路 7. 统一格式返回 Java 微服务 ...