你有没有想过,在SpringBoot集成下,Mybatis的mapper代理对象究竟是如何生成的?...
作者:青石路
cnblogs.com/youzhibing/p/10486307.html
前情回顾
之前我们讲到了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,至于如何加载此类,可参考:
https://www.cnblogs.com/youzhibing/p/9550343.html
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实例,则直接返回beanInstanceif (!(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的代理对象
至此,前情回顾中的问题也就清晰了
总结
1、自动配置的过程中,spring会扫描所有的mapper,并将所有mapper bean定义中的beanClass指向MapperFactoryBean;
2、创建mapper实例的时候,根据bean定义创建的实例实际上是MapperFactoryBean实例,然后再利用MapperFactoryBean获取mapper实例(调用MapperFactoryBean的getObject方法,mybatis会利用jdk的动态代理创建mapper代理对象);
3、对比Mybatis源码解析 - mapper代理对象的生成,你有想过吗,其实就是将我们手动创建的过程通过自动配置,将创建过程交给了spring;
补充扩展
Mybatis源码解析 - mapper代理对象的生成,你有想过吗
https://www.cnblogs.com/youzhibing/p/10451680.html
spring-boot-2.0.3启动源码篇一 - SpringApplication构造方法
https://www.cnblogs.com/youzhibing/p/9550343.html
Mybatis源码解析 - mapper代理对象的生成,你有想过吗
https://www.cnblogs.com/youzhibing/p/10451680.html
Spring拓展接口之FactoryBean,我们来看看其源码实现
https://www.cnblogs.com/youzhibing/p/10528821.html
另
琐碎时间想看一些技术文章,可以去公众号菜单栏翻一翻我分类好的内容,应该对部分童鞋有帮助。同时看的过程中发现问题欢迎留言指出,不胜感谢~。另外,有想多了解哪些方面内容的可以留言(什么时候,哪篇文章下留言都行),附菜单栏截图(PS:很多人不知道公众号菜单栏是什么)
END
我知道你 “在看”
你有没有想过,在SpringBoot集成下,Mybatis的mapper代理对象究竟是如何生成的?...相关推荐
- SpringBoot第27讲:SpringBoot集成MySQL - MyBatis 多个数据源
SpringBoot第27讲:SpringBoot集成MySQL - MyBatis 多个数据源 本文是SpringBoot第27讲,在某些场景下,Springboot需要使用多个数据源,以及某些场景 ...
- 在Springboot环境下,使用Docx4J + Freemarker 完成word docx文件生成与Pdf文件转换(附带兼容linux字体问题处理办法)
在Springboot环境下,使用Docx4J + Freemarker 完成word docx文件生成与Pdf文件转换(附带兼容linux字体问题处理办法) 前言 效果展示 正文 docx文件模板创 ...
- Springboot环境下mybatis配置多数据源配置
mybatis多数据源配置(本文示例为两个),方便实现数据库的读写分离,分库分表功能 本文基于springboot2进行的配置,如版本为springboot1系列则需修改yml的配置(在文末附带) m ...
- SpringBoot集成Actuator健康指示器health
1.说明 本文详细介绍Actuator提供的HealthIndicators, 即健康指示器的配置使用, 利用自动配置的健康指标, 检查正在运行的应用程序的状态, 以及自定义健康指标的方法. 监控软件 ...
- SpringBoot集成Actuator端点配置
1.说明 Actuator端点可以监控应用程序并与之交互. Spring Boot包括许多内置的端点, 比如health端点提供基本的应用程序运行状况信息, 并允许添加自定义端点. 可以控制每个单独的 ...
- SpringBoot集成百度uid-generator唯一ID生成器
大家好,我是猿人(猿码天地创始人),今天给码农们或即将成为码农或想成为码农的朋友讲讲SpringBoot集成百度uid-generator唯一ID生成器,现在是深夜23:10分,猿人最擅长熬夜,就是不 ...
- SpringBoot集成Maven工程
1.说明 通过Maven向导创建一个Maven Project, 而非Spring Boot向导创建Spring Starter Project, 然后通过手工修改的方式, 使这个Maven工程支持S ...
- SpringBoot集成Actuator监控管理
1.说明 本文详细介绍Spring Boot集成Actuator监控管理的方法, 基于已经创建好的Spring Boot工程, 然后引入Actuator依赖, 介绍监控管理相关功能的使用. Sprin ...
- 【水滴石穿】SpringBoot 集成Swagger
SpringBoot 集成Swagger Swagger 可以使你的restful接口自动生成接口文档,方便查看与测试,下面是一些具体步骤: 1.引入swagger相关依赖 <dependenc ...
最新文章
- 如何写新的Python OP
- 外部NORFlash是第一个以硬件为基础的信任
- 你的企业OKR实施的准备程度怎么样?快看北极星TCM团队OKR教练用的“4S检查法”
- Xilinx ISE 开发过程中生成的各种文件(二)
- Linux 升级 Python 至 3.x
- Apache Flink在 bilibili 的多元化探索与实践
- 计算机文化基础 电大,电大计算机文化基础形考一答案
- C++描述杭电OJ 2018.母牛的故事 ||
- 【数据结构与算法】二叉搜索树V2.0的Java实现
- Ubuntu 下配置lamp环境
- 服务器响应速度是上行速度吗,服务器带宽与速度之间的关系
- 特斯拉股东要用“独立董事长”换掉马斯克,不过最终失败了
- Java 多线程 之 wait等待 线程实例
- ConcurrentHashMap内部原理浅析
- 复习-arrary和arraylist的对比以及arraylist的遍历中删除的原理
- 设计模式(建造者模式)
- 查看Eclipse版本号,及各个版本区别
- 《善用佳软:高效能人士的软件应用之道》一2.6 小工具之计算器
- Codeforces Problem-705A Hulk
- matlab三维绘图
热门文章
- 一加7T Pro渲染图曝光:后置相机出乎意料
- iPhone 11运存、电池确定!5499元值得买吗?
- 造车厂入局网约车 新能源低运营成本或打破“烧钱”怪圈
- 21.55万元起的“电影周边”!现代漫威联名钢铁侠车型开售
- 阿里巴巴张勇:创造风口而不是追逐
- 有毒!OPPO Reno证件照曝光:“铡刀式”前置摄像头抢眼
- 华为P30Pro开箱照曝光 四摄模组+徕卡镜头确认
- 2019胡润全球富豪榜发布:最有钱的华人还是他!
- 力压华为小米!安兔兔1月安卓性能榜第一名是它!
- 深入理解异步Web服务器 Tornado