Mybatis源码阅读(四)-Spring集成Mybatis-Mapper接口的注入:MapperScannerConfigurer

一、Spring包扫描注解注入会过滤掉接口类

在Spring IOC容器启动的过程中,Spring会在扫描@CompopnentScan指定的路径时,会将被@Component,@Service等注解的类自动注册BeanDefinition到Spring IOC容器中,但是会过滤掉接口、抽象类,无法生成BeanDefinition,具体的源码可以查看之前的一篇文章《Spring源码阅读(四)-注册BeanDefinition-ConfigurationClassPostProcessor》的第三点。但是Mybatis 的Mapper类是接口,那么mybatis-spring又是如何将Mapper接口类注入到Sprig IOC容器的呢?接下来就要看下这个类-MapperScannerConfigurer做了什么。

二、Mapper接口的注入-MapperScannerConfigurer

注意:MapperScannerConfigurer是xml配置的Mapper接口包扫描解析类,如果是注解配置的Mapper接口则用的包扫描类是MapperScannerRegistrar,扫描解析逻辑都是一样的。我们就来分析下MapperScannerConfigurer。

常见的Mybatis.xml配置文件中,一般是这样配置包扫描的:

<!-- 扫描dao -->  
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
	<property name="basePackage" value="com.test.dao"/>  
</bean>

再来看下MapperScannerConfigurer的类的继承关系:
在这里插入图片描述

由上图可知,MapperScannerConfigurer和上面第一点说的ConfigurationClassPostProcessor类一样都是BeanFactoryPostProcessor,所以都会在Spring IOC容器在启动刷新的时候,执行AbstractApplicationContext#invokeBeanFactoryPostProcessors()方法时调用它的postProcessBeanDefinitionRegistry()方法。所以MapperScannerConfigurer是专门给Mybatis的Mapper接口类进行包扫描的类,和Spring IOC自己的包扫描类ConfigurationClassPostProcessor的处理逻辑是有区别的,那我们就来看下具体是什么区别?

看下MapperScannerConfigurer#postProcessBeanDefinitionRegistry源码:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

	// 创建一个mybaits-spring 自己的BeanDefinition扫描器来扫描Mapper接口
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

再来看下ClassPathMapperScanner的继承关系:
在这里插入图片描述

由图可知,ClassPathMapperScanner也是ClassPathBeanDefinitionScanner的子类,它分别重写了ClassPathBeanDefinitionScanner的doScan()和ClassPathScanningCandidateComponentProvider的isCandidateComponent()方法,而重点就是这个重写方法isCandidateComponent(),它允许Mapper接口注册成BeanDefinition!ClassPathMapperScanner#isCandidateComponent()方法源码如下:

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }

将Mpapper接口注册成BeanDefinition之后,再来看下它又是如何"偷梁换柱"把他从接口类,变成代理类的。看下ClassPathMapperScanner#doScan()方法源码:

public Set<BeanDefinitionHolder> doScan(String... basePackages) {

	// 重写isCandidateComponent,将Mapper接口注册成BeanDefinition
    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 {
	
	  // "偷梁换柱"将Mapper接口变成代理类
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

ClassPathMapperScanner#processBeanDefinitions()方法源码如下:

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }
      
	  // 将Mapper接口传入MapperFactoryBean的构造函数中,后面MapperProxy创建Mapper的动态代理时会用到。
      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
	  
	  // “偷梁换柱”
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
      
      	// 给MapperFactoryBean的sqlSessionFactory属性赋值
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      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.");
        }
 		// 给MapperFactoryBean的sqlSessionTemplate属性赋值
        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);
      }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值