在SpringBoot中,可以通过两种方式加载Mapper接口。

  • 通过MapperScan注解的方式加载mapper接口。
  • 如果没有注入任何的MapperFactoryBean,则使用@Mapper注解的单个注入方式。

加载Mapper接口 - 图1

@MapperScan注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(MapperScannerRegistrar.class)
  5. public @interface MapperScan {
  6. //other code...
  7. }

如果使用了@MapperScan注解,则会使用MapperScannerRegistrar加载Mapper。

  1. public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  2. private ResourceLoader resourceLoader;
  3. @Override
  4. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  5. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  6. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  7. // this check is needed in Spring 3.1
  8. if (resourceLoader != null) {
  9. scanner.setResourceLoader(resourceLoader);
  10. }
  11. Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
  12. if (!Annotation.class.equals(annotationClass)) {
  13. scanner.setAnnotationClass(annotationClass);
  14. }
  15. Class<?> markerInterface = annoAttrs.getClass("markerInterface");
  16. if (!Class.class.equals(markerInterface)) {
  17. scanner.setMarkerInterface(markerInterface);
  18. }
  19. Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
  20. if (!BeanNameGenerator.class.equals(generatorClass)) {
  21. scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
  22. }
  23. Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
  24. if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
  25. scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
  26. }
  27. scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
  28. scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
  29. List<String> basePackages = new ArrayList<String>();
  30. //解析@MapperScan的属性
  31. for (String pkg : annoAttrs.getStringArray("value")) {
  32. if (StringUtils.hasText(pkg)) {
  33. basePackages.add(pkg);
  34. }
  35. }
  36. for (String pkg : annoAttrs.getStringArray("basePackages")) {
  37. if (StringUtils.hasText(pkg)) {
  38. basePackages.add(pkg);
  39. }
  40. }
  41. for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
  42. basePackages.add(ClassUtils.getPackageName(clazz));
  43. }
  44. scanner.registerFilters();
  45. //进行包扫描
  46. scanner.doScan(StringUtils.toStringArray(basePackages));
  47. }
  48. @Override
  49. public void setResourceLoader(ResourceLoader resourceLoader) {
  50. this.resourceLoader = resourceLoader;
  51. }
  52. }

一旦使用@MapperScan注解,则回在MapperScannerRegistrar#registerBeanDefinitions方法中会解析@MapperScan注解的属性,加载basePackages,并进行包扫描,调用doScan方法。

  1. public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  2. //other code...
  3. private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
  4. @Override
  5. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  6. //调用父类的doScan方法
  7. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  8. if (beanDefinitions.isEmpty()) {
  9. logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  10. } else {
  11. //进行Bean定义
  12. processBeanDefinitions(beanDefinitions);
  13. }
  14. return beanDefinitions;
  15. }
  16. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  17. GenericBeanDefinition definition;
  18. for (BeanDefinitionHolder holder : beanDefinitions) {
  19. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  20. if (logger.isDebugEnabled()) {
  21. logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
  22. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  23. }
  24. // the mapper interface is the original class of the bean
  25. // but, the actual class of the bean is MapperFactoryBean
  26. definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
  27. //将@MapperScan扫描进来的Mapper接口的class设置为MapperFactoryBean的class
  28. definition.setBeanClass(this.mapperFactoryBean.getClass());
  29. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  30. boolean explicitFactoryUsed = false;
  31. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  32. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  33. explicitFactoryUsed = true;
  34. } else if (this.sqlSessionFactory != null) {
  35. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  36. explicitFactoryUsed = true;
  37. }
  38. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  39. if (explicitFactoryUsed) {
  40. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  41. }
  42. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  43. explicitFactoryUsed = true;
  44. } else if (this.sqlSessionTemplate != null) {
  45. if (explicitFactoryUsed) {
  46. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  47. }
  48. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  49. explicitFactoryUsed = true;
  50. }
  51. if (!explicitFactoryUsed) {
  52. if (logger.isDebugEnabled()) {
  53. logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  54. }
  55. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  56. }
  57. }
  58. }
  59. }

上面的代码definition.setBeanClass(this.mapperFactoryBean.getClass()); 把@MapperScan包扫描进来的Mapper接口的class设置成来MapperFactoryBean的class,所以使用@MapperScan注解的话,在容器中就存在MapperFactoryBean这个类型的Bean,从而导致MapperScannerRegistrarNotFoundConfiguration配置类不生效,原因就是类签名上有个注解@ConditionalOnMissingBean(MapperFactoryBean.class)。

@Mapper注解

MybatisAutoConfiguration自动配置类中的静态内部类 AutoConfiguredMapperScannerRegistrar。
在 MybatisAutoConfiguration解析MapperScannerRegistrarNotFoundConfiguration时。其条件为 @ConditionalOnMissingshiBean(MapperFactoryBean.class)。

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration implements InitializingBean {

  public static class AutoConfiguredMapperScannerRegistrar
      implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private BeanFactory beanFactory;

    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //packages AutoConfigurationPackages的加载
      List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      if (this.resourceLoader != null) {
        scanner.setResourceLoader(this.resourceLoader);
      }
      scanner.setAnnotationClass(Mapper.class);
      scanner.registerFilters();
      scanner.doScan(StringUtils.toStringArray(packages));

    }

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

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

  @org.springframework.context.annotation.Configuration
  @Import({ AutoConfiguredMapperScannerRegistrar.class })
  @ConditionalOnMissingshiBean(MapperFactoryBean.class)
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
      logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
  }
}