Mybatis源码(一)利用springboot集成以及Mapper接口加载过程

从本篇文章开始,简单分析下mybatis源码。
本篇文章先分析下mapper接口是如何被扫描到spring容器中的。

springboot集成mybatis非常简单,只需要在pom文件中添加如下依赖(本文分析的是1.3.2版本):

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>1.3.2</version>
 </dependency>

1. mybatis如何被加载启动

在使用mybatis时,我们写了很多的mapper接口,接口上面使用了@Mapper注解修饰;但是此注解是mybais中的内容,并非spring,也就是说spring并不认识此注解,那么这些mapper接口是如何被加载到spring容器中的呢?

这部分内容,实际上是springboot的内容,至于springboot的启动流程以及原理,如果不清楚可以先看下之前的分析文章《springboot源码(一)启动流程+自动配置原理分析》

从这篇文章中,我们知道springboot默认会加载starter包中的spring.factories文件,并读取其中的配置类。

上述mybatis-spring-boot-starter中并没有这个spring.factories文件,但是当我们打开这个pom文件,发现它有如下依赖:

<dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>

从依赖的libraries中扎到此依赖,打开,发现存在spring.factories文件:
在这里插入图片描述
继续打开此文件:
在这里插入图片描述
发现只有一个配置类MybatisAutoConfiguration,没错,入口就在这里了。

2. MybatisAutoConfiguration

这是一个被@Configuration注解修饰bean,是配置类。
看一下类结构:
在这里插入图片描述
内容不多,包含两个内部类,四个方法以及一些属性。
先简单介绍他们的作用:

  • MybatisAutoConfiguration():构造方法;
  • checkConfigFileExists():检查是否需要加载存在的mybatis的config xml配置文件,有就加载;
  • sqlSessionFactory(): 初始化SqlSessionFactory对象,这个是Mybatis的核心类之一。
  • sqlSessionTemplate(): 初始化SqlSessionTemplate对象;
  • MapperScannerRegistrarNotFoundConfiguration:这是一个内部类,也是配置类,核心是上面的@import注解,内容是下面的内部类AutoConfiguredMapperScannerRegistrar;
  @org.springframework.context.annotation.Configuration
  @Import({ AutoConfiguredMapperScannerRegistrar.class })
  @ConditionalOnMissingBean(MapperFactoryBean.class)
  public static class MapperScannerRegistrarNotFoundConfiguration {
    @PostConstruct
    public void afterPropertiesSet() {
      logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
  }
  • AutoConfiguredMapperScannerRegistrar:也是个内部类,提供了扫描Mapper接口的方法;

我们看下这个AutoConfiguredMapperScannerRegistrar,其中有一个方法:

	@Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//扫描被@Mapper修饰的mapper接口
      logger.debug("Searching for mappers annotated with @Mapper");

      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }
		//得到要扫描的包
        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        if (logger.isDebugEnabled()) {
          for (String pkg : packages) {
            logger.debug("Using auto-configuration base package '{}'", pkg);
          }
        }
		//设置只扫描@Mapper注解修饰的bean
        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        //执行扫描动作
        scanner.doScan(StringUtils.toStringArray(packages));
      } catch (IllegalStateException ex) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
      }
    }

此方法名字应该很熟悉了,他是ImportBeanDefinitionRegistrar接口方法的实现(不熟悉这个类的可以看下使用场景《spring @Import注解的作用和几种使用方式》);

registerBeanDefinitions这个方法,看名字就知道它的作用是注册bean定义,就是往spring容器中注册,注册的bean,就是mapper接口。作者通过第一行log打印出了此方法的功能:Searching for mappers annotated with @Mapper。

到这里,我们基本已经知道了mybatis在springboot中到底是如何被扫描并注册的。

下面我们详细看下。

3. AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions

上面已经贴出了此方法的代码,发现主要是通过ClassPathMapperScanner类来完成具体扫描动作。
其中核心方法是 scanner.doScan(StringUtils.toStringArray(packages));

@Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //通过父类方法扫描
    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 {
    //处理bean定义
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

首先调用父类方法扫描所有mapper,并得到bean定义的集合;
然后,通过processBeanDefinitions(beanDefinitions);又对bean的定义进行了二次加工:

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //遍历每个mapper接口的bean定义进行加工处理
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      //构造函数中掺入mapper的名字
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      //每个mapper接口,都变为了MapperFactoryBean;但是会通过上一行的构造函数参数(mapper名字)来区分每个mapper
      definition.setBeanClass(this.mapperFactoryBean.getClass());
	  //添加了一个属性addToConfig,值为true
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      //下面两个if进不去
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }
		//下面两个if进不去
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        //注意设置的自动注入类型
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

此方法修改了mapper的bean定义,比较重要的有:

  • 重置了beanClass为代理类 MapperFactoryBean;此时mapper接口已经物是人非成为别人了。
  • 因为每个mapper都变成了MapperFactoryBean,为了区分他们,MapperFactoryBean的构造函数参数,传入了mapper接口的名字;
  • sqlSessionFactory属性和sqlSessionTemplate属性注意下,虽然这里还没把具体属性设置进去,但是实例化的mapper的时候会用到这俩;
  • *definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);设置注入类型为按类型注入;sqlSessionFactory属性和sqlSessionTemplate属性就是因此才set注入的。

上述过程只是bean定义过程,这俩属性会在mapper bean实例化的之后,进行填充;

上面这几点比较重要,后续文章动态代理mapper接口都跟他们有关系。

本文先简单分析到这里,主要分析了mapper的被加载流程,到这里mapper已经被注册了,
至于mapper接口的实例化以及如何被动态代理以及sql如何执行等详细内容,放在后续文章中分析:

发布了62 篇原创文章 · 获赞 29 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/csdn_20150804/article/details/102690824