@MapperScan注解原理解析 --> 1.3版本和2.0版本的区别

@MapperScan注解是spring和mybatis整合的关键注解,这个注解的作用也简单,就是为了把当前mapper接口,放到spring容器中,但是这篇笔记想要记录的是,@MapperScan注解在1.3版本和2.0版本中的区别; 这个注解是放在mybatis-spring这个jar包中

1.3版本

在1.3版本中,通过@Import注解引入了一个bean:

MapperScannerRegistrar

@MapperScan注解原理解析 --> 1.3版本和2.0版本的区别_第1张图片

这里可以看到,这个bean是ImportBeanDefinitionRegistrar的实现类,spring源码:@Import注解 在这篇博客中有介绍,这个接口的作用,简单来说,我们实现这个接口之后,可以拿到BeanDefinitionRegistry对象,可以自己去声明一个beanDefinition对象,放到beanDefinitionMap中,然后spring容器会帮我们完成初始化的操作
我们接着来看实现类中的实现方法

@Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    Class annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

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

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    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));
  }

可以看到,这里直接初始化了一个scanner对象,然后根据当前要扫描的包,去扫描mapper接口,doScan方法中就和spring扫描@Component注解的代码是类似的,这篇博客中不展开详细叙述,我们只需要知道,这里会帮我们把所有的mapper接口扫描出来,然后根据扫描结果,初始化beanDefinition对象

2.X版本

在2.X版本中,虽然@MapperScan注解也是注入了ImportBeanDefinitionRegistry的实现类,但是在实现方法中,逻辑不同,可以看到,在2.X版本中,并没有初始化scanner,然后去扫码,而是继续注册了一个bean:

MapperScannerConfigurer

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {

    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);

    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }

我们接着来看MapperScannerConfigurer这个bean的作用

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

首先我们可以看到,MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的实现类,
@MapperScan注解原理解析 --> 1.3版本和2.0版本的区别_第2张图片
关于这个接口的作用,在前面博客中有介绍过,我们直接来看实现类的实现方法中做了什么

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

    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));
  }

可以发现,这里是真正去扫描mapper接口的地方,扫描之后,然后注册为beanDefinition对象

总结

所以,最后再总结一下,不管是哪个版本,@MapperScan注解都是注入了一个beanDefinitionRegistry的实现类,不同的是:

  1. 在1.3版本中,MapperScannerRegistrar的registerBeanDefinitions方法中,会自己初始化一个扫描器,然后去扫描mapper接口,将扫描到的接口,初始化为beanDefinition对象,将beanDefinition对象的beanClass设置为mapperFactoryBean
  2. 但是在2.X版本中,@MapperScan注解虽然注入的也是MapperScannerRegistrar对象,但是这个类中,接着又注入了一个BeanDefinitionRegistryPostProcessor的实现类,在其实现类中,完成了mapper的扫描

所以,在2.X版本中,额外利用到了BeanDefinitionRegistryPostProcessor的扩展机制

你可能感兴趣的:(mybatis源码,mybatis,java,spring)