作者:青石路

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代理对象究竟是如何生成的?...相关推荐

  1. SpringBoot第27讲:SpringBoot集成MySQL - MyBatis 多个数据源

    SpringBoot第27讲:SpringBoot集成MySQL - MyBatis 多个数据源 本文是SpringBoot第27讲,在某些场景下,Springboot需要使用多个数据源,以及某些场景 ...

  2. 在Springboot环境下,使用Docx4J + Freemarker 完成word docx文件生成与Pdf文件转换(附带兼容linux字体问题处理办法)

    在Springboot环境下,使用Docx4J + Freemarker 完成word docx文件生成与Pdf文件转换(附带兼容linux字体问题处理办法) 前言 效果展示 正文 docx文件模板创 ...

  3. Springboot环境下mybatis配置多数据源配置

    mybatis多数据源配置(本文示例为两个),方便实现数据库的读写分离,分库分表功能 本文基于springboot2进行的配置,如版本为springboot1系列则需修改yml的配置(在文末附带) m ...

  4. SpringBoot集成Actuator健康指示器health

    1.说明 本文详细介绍Actuator提供的HealthIndicators, 即健康指示器的配置使用, 利用自动配置的健康指标, 检查正在运行的应用程序的状态, 以及自定义健康指标的方法. 监控软件 ...

  5. SpringBoot集成Actuator端点配置

    1.说明 Actuator端点可以监控应用程序并与之交互. Spring Boot包括许多内置的端点, 比如health端点提供基本的应用程序运行状况信息, 并允许添加自定义端点. 可以控制每个单独的 ...

  6. SpringBoot集成百度uid-generator唯一ID生成器

    大家好,我是猿人(猿码天地创始人),今天给码农们或即将成为码农或想成为码农的朋友讲讲SpringBoot集成百度uid-generator唯一ID生成器,现在是深夜23:10分,猿人最擅长熬夜,就是不 ...

  7. SpringBoot集成Maven工程

    1.说明 通过Maven向导创建一个Maven Project, 而非Spring Boot向导创建Spring Starter Project, 然后通过手工修改的方式, 使这个Maven工程支持S ...

  8. SpringBoot集成Actuator监控管理

    1.说明 本文详细介绍Spring Boot集成Actuator监控管理的方法, 基于已经创建好的Spring Boot工程, 然后引入Actuator依赖, 介绍监控管理相关功能的使用. Sprin ...

  9. 【水滴石穿】SpringBoot 集成Swagger

    SpringBoot 集成Swagger Swagger 可以使你的restful接口自动生成接口文档,方便查看与测试,下面是一些具体步骤: 1.引入swagger相关依赖 <dependenc ...

最新文章

  1. 如何写新的Python OP
  2. 外部NORFlash是第一个以硬件为基础的信任
  3. 你的企业OKR实施的准备程度怎么样?快看北极星TCM团队OKR教练用的“4S检查法”
  4. Xilinx ISE 开发过程中生成的各种文件(二)
  5. Linux 升级 Python 至 3.x
  6. Apache Flink在 bilibili 的多元化探索与实践
  7. 计算机文化基础 电大,电大计算机文化基础形考一答案
  8. C++描述杭电OJ 2018.母牛的故事 ||
  9. 【数据结构与算法】二叉搜索树V2.0的Java实现
  10. Ubuntu 下配置lamp环境
  11. 服务器响应速度是上行速度吗,服务器带宽与速度之间的关系
  12. 特斯拉股东要用“独立董事长”换掉马斯克,不过最终失败了
  13. Java 多线程 之 wait等待 线程实例
  14. ConcurrentHashMap内部原理浅析
  15. 复习-arrary和arraylist的对比以及arraylist的遍历中删除的原理
  16. 设计模式(建造者模式)
  17. 查看Eclipse版本号,及各个版本区别
  18. 《善用佳软:高效能人士的软件应用之道》一2.6 小工具之计算器
  19. Codeforces Problem-705A Hulk
  20. matlab三维绘图

热门文章

  1. 一加7T Pro渲染图曝光:后置相机出乎意料
  2. iPhone 11运存、电池确定!5499元值得买吗?
  3. 造车厂入局网约车 新能源低运营成本或打破“烧钱”怪圈
  4. 21.55万元起的“电影周边”!现代漫威联名钢铁侠车型开售
  5. 阿里巴巴张勇:创造风口而不是追逐
  6. 有毒!OPPO Reno证件照曝光:“铡刀式”前置摄像头抢眼
  7. 华为P30Pro开箱照曝光 四摄模组+徕卡镜头确认
  8. 2019胡润全球富豪榜发布:最有钱的华人还是他!
  9. 力压华为小米!安兔兔1月安卓性能榜第一名是它!
  10. 深入理解异步Web服务器 Tornado