MyBatis注解版分析(2)

MyBatis注解版分析(2)_第1张图片
image.png

前面我们说到在创建SqlSessionFactory的时候会一并把mapper文件解析,但是如果使用@Mapper注解类会单独使用AutoConfigUredMapperScannerRegistrar内部类来扫描。


  @org.springframework.context.annotation.Configuration
  //把当前类导入配置类中,并且把当前类的对象注入到容器中
  @Import({ AutoConfiguredMapperScannerRegistrar.class }) 
  //仅当容器中不存在MapperFactoryBean实例不存在的时候才创建@Import导入的类的实例
  @ConditionalOnMissingBean(MapperFactoryBean.class)
  public static class MapperScannerRegistrarNotFoundConfiguration {
    //servlet容器初始化的时候执行一次
    @PostConstruct
    public void afterPropertiesSet() {
      logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
  }

//存在MyBatisAutoConfigUration中的内部类,这个类只会在启动类没有贴上@MapperScan的时候才会执行
/**
*  这个类实现了三个接口
*  BeanFactoryAware:通过调用这个接口,实现里面的方法可以获取存放这个
*
*/
public static class AutoConfiguredMapperScannerRegistrar
      implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private BeanFactory beanFactory;
    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      logger.debug("Searching for mappers annotated with @Mapper");
      //这个类是扫描@Mapper注解的关键类
      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }
        //得到@autoConfiguration所在类的包,我这里是com.miaowenzhao.blogs
        List packages = AutoConfigurationPackages.get(this.beanFactory);
        if (logger.isDebugEnabled()) {
          for (String pkg : packages) {
            logger.debug("Using auto-configuration base package '{}'", pkg);
          }
        }
        //设置要扫描的注解类
        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);
      }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.beanFactory = beanFactory;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader = resourceLoader;
    }
  }
  • 首先通过@ImportAutoConfigUredMapperScannerRegistrar注入到容器并立即调用实现方法registerBeanDefinitions(),这个方法内部创建了一个ClassPathMapperScanner对象进行@Mapper类的扫描
  • 另外向容器注入MapperFactoryBean对象,这个专门用于将扫描到的类创建代理对象,并注入到容器中。
  • scanner.registerFilters();具体看这个方法
    public void registerFilters() {
    //当他为true表示要扫描所有接口
    boolean acceptAllInterfaces = true;
    
    // 这里判断我们有没有传入注解类,也就是上面代码传入的Mapper.class
    if (this.annotationClass != null) {
      //这是父类的方法,跟表示只扫描Mapper.class注解所标注的类
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
      //只要找到一个就设置为false
      acceptAllInterfaces = false;
    }
    
    // override AssignableTypeFilter to ignore matches on the actual marker interface
    if (this.markerInterface != null) {
      //传入具体的接口路径
      addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
        @Override
        protected boolean matchClassName(String className) {
          return false;
        }
      });
      acceptAllInterfaces = false;
    }
    
    if (acceptAllInterfaces) {
      // default include filter that accepts all classes
      addIncludeFilter(new TypeFilter() {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
          return true;
        }
      });
    }
    
    

如果我用@MapperScan呢?

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) //这里导入这个类,这个类是关键
public @interface MapperScan {}

MapperScannerRegistrar.class

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  private ResourceLoader resourceLoader;

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   //通过这个方法可以把注解中的所有封装成AnnotationAttributes extend LinkedHashMap
   AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
   //这里仍然是这个类进行扫包
   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    //...中间都是一些赋值操作
    //这里会设置进MapperFactoryBean,并且会注入到容器中
    Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }
    List basePackages = new ArrayList();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));

  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }
}
  • 这里我们真正体会到其实具体的扫包、注入功能都是在ClassPathMapperScanner类中实现
  • 设置了@MapperScan后将会忽略@Mapper

进入scanner.doScan(StringUtils.toStringArray(packages));方法

  @Override
  public Set doScan(String... basePackages) {
    //通过调用父类的doScan方法传入参数成功返回一个set集合,这个集合就保存贴有@Mapper注解的类路径
    Set 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;
  }
  • 通过调用super.doScan(basePackages);扫描出映射类,并存放到Set集合中

进入processBeanDefinitions(beanDefinitions);

private void processBeanDefinitions(Set beanDefinitions) {
    GenericBeanDefinition definition;
    //这里for循环,配置每个Mapper类的属性并设置进GenericBeanDefinition 
    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
      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) {
        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() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

通过源码我们可以总结出结论

  • 如果设置了@MapperScan将会忽略@Mapper,只会扫描@MapperScan中用户指定的包路径

  • 具体的扫包、注入功能都是在ClassPathMapperScanner类中实现,这是MyBatis关键类

  • @Import({ AutoConfiguredMapperScannerRegistrar.class })直接将类注入到容器中,这个类可以实现ImportBeanDefinitionRegistrar,重写方法后会在方法中获取BeanDefinitionRegistry,可以自定义注册。

  • @ConditionalOnMissingBean(MapperFactoryBean.class)按条件注入,贴在容器上时,表示当容器中存在当前类的实例则容器中的Bean都不会注入到容器中。当首先创建MapperScannerRegistrar的时候就向容器中注入了MapperFactoryBean

     Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
     if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
       scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
     }
    

    则下面的@Import无法注入

    @org.springframework.context.annotation.Configuration
    //把当前类导入配置类中,并且把当前类的对象注入到容器中
    @Import({ AutoConfiguredMapperScannerRegistrar.class }) 
    //仅当容器中不存在MapperFactoryBean实例不存在的时候才创建@Import导入的类的实例
    @ConditionalOnMissingBean(MapperFactoryBean.class)
    public static class MapperScannerRegistrarNotFoundConfiguration {
    @PostConstruct
     public void afterPropertiesSet() {
       logger.debug("No {} found.", MapperFactoryBean.class.getName());
     }
    }
    

MyBatis注解版分析(1).
MyBatis注解版分析(3).

你可能感兴趣的:(MyBatis注解版分析(2))