@MapperScan配置在@Configuration注解的类上会导入MapperScannerRegistrar类。

而MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,可以向BeanDefinitionRegistry注册BeanDefinition。

  1. public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  2. private ResourceLoader resourceLoader;
  3. @Override
  4. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  5. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  6. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  7. //略。。。
  8. List<String> basePackages = new ArrayList<String>();
  9. for (String pkg : annoAttrs.getStringArray("value")) {
  10. if (StringUtils.hasText(pkg)) {
  11. basePackages.add(pkg);
  12. }
  13. }
  14. for (String pkg : annoAttrs.getStringArray("basePackages")) {
  15. if (StringUtils.hasText(pkg)) {
  16. basePackages.add(pkg);
  17. }
  18. }
  19. for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
  20. basePackages.add(ClassUtils.getPackageName(clazz));
  21. }
  22. scanner.registerFilters();
  23. scanner.doScan(StringUtils.toStringArray(basePackages));
  24. }
  25. }

从上述源码可以看到MapperScannerRegistrar的registerBeanDefinitions,从mapperScan注解获取到要扫描的包,将扫描路径提交给ClassPathMapperScanner的doScan方法。

ClassPathMapperScanner继承于ClassPathBeanDefinitionScanner,并覆盖doScan方法,通过父类获取到basePackages路径下的类的beanDefinition,然后修改beanDefinition的beanClass为MapperFactoryBean。

  1. public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  2. @Override
  3. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  4. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  5. if (beanDefinitions.isEmpty()) {
  6. logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  7. } else {
  8. processBeanDefinitions(beanDefinitions);
  9. }
  10. return beanDefinitions;
  11. }
  12. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  13. GenericBeanDefinition definition;
  14. for (BeanDefinitionHolder holder : beanDefinitions) {
  15. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  16. if (logger.isDebugEnabled()) {
  17. logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
  18. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  19. }
  20. //修改构造方法参数为mapper的class
  21. definition.getConstructorArgumentValues()
  22. .addGenericArgumentValue(definition.getBeanClassName());
  23. //修改beanClass为mapperFactoryBean
  24. definition.setBeanClass(this.mapperFactoryBean.getClass());
  25. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  26. //注入sqlSessionFactory/sqlSessionTemplate
  27. boolean explicitFactoryUsed = false;
  28. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  29. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  30. explicitFactoryUsed = true;
  31. } else if (this.sqlSessionFactory != null) {
  32. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  33. explicitFactoryUsed = true;
  34. }
  35. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  36. if (explicitFactoryUsed) {
  37. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  38. }
  39. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  40. explicitFactoryUsed = true;
  41. } else if (this.sqlSessionTemplate != null) {
  42. if (explicitFactoryUsed) {
  43. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  44. }
  45. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  46. explicitFactoryUsed = true;
  47. }
  48. if (!explicitFactoryUsed) {
  49. if (logger.isDebugEnabled()) {
  50. logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  51. }
  52. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  53. }
  54. }
  55. }
  56. @Override
  57. //覆盖父类的isCandidateComponent,判断bean为接口且独立
  58. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
  59. return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  60. }
  61. }

最后通过BeanFactory实例化MapperFactoryBean时,因为MapperFactoryBean实现了FactoryBean,会调用getObject生成bean,注册到beanFactory。

  1. public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  2. private Class<T> mapperInterface;
  3. private boolean addToConfig = true;
  4. public MapperFactoryBean() {
  5. //intentionally empty
  6. }
  7. public MapperFactoryBean(Class<T> mapperInterface) {
  8. this.mapperInterface = mapperInterface;
  9. }
  10. @Override
  11. //将mapper注册到Configuration中
  12. protected void checkDaoConfig() {
  13. super.checkDaoConfig();
  14. notNull(this.mapperInterface, "Property 'mapperInterface' is required");
  15. Configuration configuration = getSqlSession().getConfiguration();
  16. if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
  17. try {
  18. configuration.addMapper(this.mapperInterface);
  19. } catch (Exception e) {
  20. logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
  21. throw new IllegalArgumentException(e);
  22. } finally {
  23. ErrorContext.instance().reset();
  24. }
  25. }
  26. }
  27. @Override
  28. //通过SqlSession获取Mapper代理
  29. public T getObject() throws Exception {
  30. return getSqlSession().getMapper(this.mapperInterface);
  31. }
  32. }

Mybatis——@MapperScan原理的更多相关文章

  1. SpringBoot集成MyBatis底层原理及简易实现

    MyBatis是可以说是目前最主流的Spring持久层框架了,本文主要探讨SpringBoot集成MyBatis的底层原理.完整代码可移步Github. 如何使用MyBatis 一般情况下,我们在Sp ...

  2. MyBatis工作原理

    Mybatis工作原理: 我们的应用程序通过mybatis提供的api,增删改查方法来访问数据库,api底层调用了jdbc ,只不过mybatis对jdbc的封装是不完全封装,里面的sql语句需要我们 ...

  3. Mybatis插件原理分析(二)

    在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...

  4. Mybatis的原理与JVM内存结构(面试题)

    Mybatis的原理 1.Mapper 接口在初始SQL SessionFactory注册的 2.Mapper 接口注册在名为MapperRegistry类的 HasMap中 key=Mapper c ...

  5. 面试官:你分析过mybatis工作原理吗?

    Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...

  6. mybatis运行原理

    mybatis运行原理 运行过程中涉及到的类或者接口 Resources(c) :用于加载mybatis核心配置文件 XMLConfigBuilder(c) :用于解析xml文件(核心配置文件) Co ...

  7. mybatis 插件原理

    [传送门]:mybatis 插件原理

  8. 【转】MaBatis学习---源码分析MyBatis缓存原理

    [原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...

  9. 深入理解MyBatis的原理:整个体系

    前言:工作中虽然用到了 MyBatis,可完全不知道为什么,再不学习就晚了,这里将记录我的学习笔记,整个 MyBatis 的体系. 一.简介 1.传统的JDBC JDBC 是一种典型的桥接模式. 使用 ...

随机推荐

  1. ElasticSearch解决深度分页性能存在的问题使用scoll来解决

    现在我们全局搜索全部的数据,每次返回3条, 从 scroll 请求返回的结果反映了 search 发生时刻的索引状态,就像一个快照.后续的对文档的改动(索引.更新或者删除)都只会影响后面的搜索请求. ...

  2. 28_链表插入和删除算法的演示.swf

    #include<stdio.h> #include<malloc.h> #include <stdio.h> #include <stdlib.h> ...

  3. java scoket Blocking 阻塞IO socket通信一

    package bhz.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; p ...

  4. Spring IoC 默认标签解析

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  5. mybatis缓存之一级缓存(一)

    对于mybatis框架.仿佛工作中一直是在copy着使用.对于mybatis缓存.并没有一个准确的认知.趁着假期.学习下mybatis的缓存.这篇主要学习mybatis的一级缓存. 为什么使用缓存 其 ...

  6. C#数据结构与算法系列(十九):选择排序算法(SelectSort)

    1.介绍 选择排序算法属于内部排序算法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置达到排序的目的 时间复杂度:O(n^2) 双层for 2.思想 选择排序(select sorti ...

  7. web开发相关概念

    什么是web通信? WEB采用B/S通信模式,通过超文本传送协议(HTTP, Hypertext transport protocol)进行通信.通过浏览器地址栏编写URL,向服务器发送一个请求,服务 ...

  8. docker 容器中 apt-get install 软件时,提示无法定位软件包

    [解决] 执行 apt-get update 然后再进行安装,即可. (完)

  9. Python3笔记010 - 3.1 程序结构

    第3章 流程控制语句 3.1 程序结构 程序设计的基本结构: 顺序结构---顺序执行所有语句 选择结构---选择执行部分语句 循环结构---循环执行部分语句 1.顺序结构 按照顺序执行语句. 2.选择 ...

  10. Java1.8的HashMap源码解析

    java1.8是现在用的最多的版本,hashmap是现在用的最多的map,今天我们试图分析一下源码. 数据结构 首先我们注意到数据是存放在一个Node数组里面 transient Node<K, ...