mybatis源码-@MapperScan

1,637 阅读3分钟

说明

  1. 接下来几篇讲的是 mybatis-spring 的源码
  2. 目前分为4篇讲完,包含如下(@MapperScan ,sqlSessionFactory,mapperFactorBean,调用过程)
  3. 可能会穿插这spring-ioc的知识
  4. 首先你要知道springboot整合mybatis是怎么操作的,不会讲怎么使用

@MapperScan

直接看代码

//一个项目整合mybatis,最少你要配置两个东西
//1.写了sql的xml文件放在哪里的
mybatis.mapperLocations=classpath:mappers/*.xml


@SpringBootApplication
//2.dao接口在哪
@MapperScan(value="com.kiss.mxb.dao")
public class Test01Application {

	public static void main(String[] args) {
		SpringApplication.run(Test01Application.class, args);
	}
}

//看下@mapperScan这个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//最最关键的就是这个@Import注解
//讲sprig-ioc 的时候说过,@Import处理三种类型的bean
//MapperScannerRegistrar 就是继承了其中一个,还是功能最强大的那个
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {}
//讲spring-ioc-@import 注解的时候说过,如果 import 的类是实现了ImportBeanDefinitionRegistrar,则执行它的registerBeanDefinitions方法
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  //就是这个方法,看些这个方法拿到了注解元数据和注册器
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //获取名字为MapperScan的注解的属性
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

   ........................................................
    //获取 mapperScan 注解的 value属性,也就是我们配置的扫描的报名
    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    //也可以用basePackages属性配置扫描包名
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    //basePackageClasses属性也可以
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    //来了老弟,去扫描
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //调用了spring的扫描的功能,以前讲spring-ioc的时候已经看过了,所以这里就不看了
    //这里里面就是把你扫描到的bean注册到 beanDefinitionMap
    //然后把这些 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 {
      //这里是重点
      processBeanDefinitions(beanDefinitions);
    }
    
    return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //循环这些beanDefinition
    for (BeanDefinitionHolder holder : beanDefinitions) {
      // 持有者转beanDefinition
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
        
        //下面的都是给这些beanDefinition设置属性
      // 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
      
      //这个地方是最骚的地方(重点)
      //把当前的beanDefinition 的 beanClas换成 mapperFactoryBean,这是啥意思呢,意思就是 本来 有一台大众,但是现在壳子还是大众,但是里面的东西 都是换成了 法拉利
      //mapperFactoryBean 实现了 FactoryBean 
      //getbean的时候获取的是 它  getObject的方法返回的bean
      definition.setBeanClass(this.mapperFactoryBean.getClass());
        
      //添加了    PropertyValues  下面的代码都不是很重要
      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) {
        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.");
        }
        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() + "'.");
        }
        //这个地方 是最最最骚的  设置当前bean的依赖注入模型
        //这个地方我科普下,我们一般使用的 @Autowired , @Resource  不清楚spring源码的都会说这两个注解 bytype byname,怎么说呢,说的对也不对.
        //其实 spring 依赖注入的模型有四种 分别为 AUTOWIRE_NO ,AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE,AUTOWIRE_CONSTRUCTOR
        //我们正常的bean都是AUTOWIRE_NO 意外吧 
        //这个地方mybatis 把这些 mapperFactoryBean 的依赖注入模型都设置为 AUTOWIRE_BY_TYPE
        //AUTOWIRE_BY_TYPE 的特征是,一个bean的依赖注入模型为AUTOWIRE_BY_TYPE时,只需要属性存在 set 方法,就会自动注入,不需要  @Autowired , @Resource 
        //在这里说这个只是让我们学到 MapperFactoryBean 的时候不会为一些事情纠结
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE
        //);
      }
    }
  }