mybatis源码

72 阅读3分钟

在项目中大多数用的都是@MapperScan注解,指定basePackages,扫描mybatis Mapper接口类,另外一种方式是用@Mapper注解,其实这两种方法扫描配置用的是一个地方,只是扫描入口不同。

@SpringBootApplication
@MapperScan("cn.xh.kaierle.admin.dao")
@EnableScheduling
public class Application

@MapperScan是根据其注解上MapperScannerRegistrar进行自动配置的,最终调用的自动配置代码和下面的代码一致

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
public @interface MapperScan


public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口可以将自定义的对象注册为spring管理的bean

image.png

@Mapper自动配置的程序入口是 MybatisAutoConfiguration类的最下面,位置在mybatis-spring-boot-starter包下面的mybatis-spring-boot-autoconfigure,根据名字可以看出,这个是自动配置,这个地方用到了spring-boot的自动配置相关注解

1 全局配置入口

上面代码的逻辑是 如果标注了@MapperScan 的注解,将会生成 MapperFactoryBean, 如果没有标注@MapperScan 也就是没有MapperFactoryBean的实例,就走@Import里面的配置,下面看看AAI的配置,它是MybatisAutoConfiguration类下的内部类

2 标记有 @mapper注解的类 扫描配置

上面的代码的逻辑就是初始化ClassPathMapperScanner扫描器,这个扫描器继承了spirng的ClassPathBeanDefinitionScanner的主要作用就是扫描Mapper接口类进行配置并注册为spring bean,首先看看核心的doScan()方法,@MapperScan注解的自动配置也是用了这个注解,这个方法做了什么,也是贴图如下:

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    public static final Logger LOGGER = LoggerFactory.getLogger(MapperScannerRegistrar.class);

    private ResourceLoader resourceLoader;

    private Environment environment;

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

        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
       // 省略代码
        scanner.registerFilters(); // 注册过滤器,过滤不需要的bean
        scanner.doScan(StringUtils.toStringArray(basePackages));
    }

scanner.registerFilters(); 作用



private final List<TypeFilter> includeFilters = new ArrayList<>();

private final List<TypeFilter> excludeFilters = new ArrayList<>();


/**
 * Determine whether the given class does not match any exclude filter
 * and does match at least one include filter.
 * @param metadataReader the ASM ClassReader for the class
 * @return whether the class qualifies as a candidate component
 */
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

3  ClassPathMapperScanner类红的核心方法 doScan()

这个类主要就是调用父类ClassPathBeanDefinitionScanner中的doScan()方法,获取所有的类定义,之后添加自己的逻辑:processBeanDefinitions()

下面看看processBeanDefinitions()方法主要做了什么,也是贴出核心代码


private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();

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

        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); 
        //设置bean真正的class类型MapperFactoryBean
        definition.setBeanClass(this.mapperFactoryBean.getClass());
        //设置通用 Mapper
        if(StringUtils.hasText(this.mapperHelperBeanName)){
            definition.getPropertyValues().add("mapperHelper", new RuntimeBeanReference(this.mapperHelperBeanName));
        } else {
            //不做任何配置的时候使用默认方式
            if(this.mapperHelper == null){
                this.mapperHelper = new MapperHelper();
            }
            definition.getPropertyValues().add("mapperHelper", this.mapperHelper);
        }

        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) {
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
    }
}

4 ClassPathMapperScanner类中处理类定义的核心方法processBeanDefinitions()

注意上面涂黄的代码,这个方法主要做的事情就是重新设置上面扫描出的BeanDefinition,设置构造器的参数,构造器的参数为标注了@Mapper的类的class,

做的事情其实是重写BeanDefinition的BeanClass字段为MapperFactoryBean.class,并且将beanClass其实也就是MapperFactoryBean的构造器参数设置为实际的标注了@Mapper的接口,之后当Mapper接口注入的时候,实际调用的是MapperFactoryBean中的getObject()获取特定的mapper实例

如下是MapperFactoryBean的核心逻辑,mapperInterface字段是通过上面的 

definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());这段代码注入的

5 MapperFactoryBean的核心代码

可见这个类实现了FactoryBean的接口(自由拓展注入的类),以及继承了SqlSessionDaoSupport,继承SqlSessionDaoSupport的主要目的是为了获取SqlSession,通过SqlSession获取具体的mapper代理类(这个也是核心方法)


public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    // intentionally empty
  }

  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSingleton() {
    return true;
  }
  }

综上所述,首先根据标注的@MapperScan 获取basePackage或者根据@Mapper获取所在packages,之后通过 ClassPathMapperScanner去扫描包,获取所有Mapper接口类的BeanDefinition,之后具体配置,设置beanClass为MapperFactoryBean,设置MapperFactoryBean的构造器参数为实际的Mapper接口类,通过ClassPathBeanDefinitionScanner父类进行bean注册,自动注入的时候,就会调用MapperFactoryBean的getObject方法获取实际类型的实例。