精尽MyBatis源码分析 - MyBatis-Spring 源码分析

该系列文档是本人在学习 Mybatis 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释(Mybatis源码分析 GitHub 地址Mybatis-Spring 源码分析 GitHub 地址Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读html

MyBatis 版本:3.5.2java

MyBatis-Spring 版本:2.0.3git

MyBatis-Spring-Boot-Starter 版本:2.1.4github

在前面的一系列文档中对整个 MyBatis 框架进行了分析,相信你对 MyBatis 有了一个更加深刻的了解。在使用它的过程当中,须要本身建立 SqlSessionFactory 和 SqlSession,而后获取到 Mapper 接口的动态代理对象,执行数据库相关操做,对这些对象的管理并非那么简单。咱们一般会结合 Spring 来使用 MyBatis,将这些对象做为 Spring Bean 注入到 Spring 容器,也容许参与到 Spring 的事务管理之中spring

Spring 官方并无提供对 MyBatis3 的集成方案,因而在 MyBatis 社区将对 Spring 的集成做为一个 MyBatis 子项目 MyBatis-Spring,帮助你将 MyBatis 代码无缝地整合到 Spring 中,那么咱们一块儿来看看这个子项目是如何集成到 Spring 中的sql

在开始读这篇文档以前,须要对 Spring 有必定的了解,能够结合个人源码注释(Mybatis-Spring 源码分析 GitHub 地址)进行阅读,MyBatis-Spring官方文档数据库

简述

主要涉及到的几个类:apache

  • org.mybatis.spring.SqlSessionFactoryBean:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象数组

  • org.mybatis.spring.mapper.MapperFactoryBean:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象缓存

  • org.mybatis.spring.support.SqlSessionDaoSupport抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象

  • org.mybatis.spring.mapper.MapperScannerConfigurer:实现了BeanDefinitionRegistryPostProcessor、InitializingBean接口,ApplicationContextAware、BeanNameAware接口,用于扫描Mapper接口,借助ClassPathMapperScanner扫描器对Mapper接口的BeanDefinition对象(Spring Bean 的前身)进行修改

  • org.mybatis.spring.mapper.ClassPathMapperScanner:继承了ClassPathBeanDefinitionScanner抽象类,负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改成 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean 类型,实现建立 Mapper 动态代理对象

  • org.mybatis.spring.annotation.MapperScannerRegistrar:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口

    做为@MapperScann 注解的注册器,根据注解信息注册一个 MapperScannerConfigurer 对象,用于扫描 Mapper 接口

  • org.mybatis.spring.config.MapperScannerBeanDefinitionParser:实现 BeanDefinitionParser 接口,<mybatis:scan /> 的解析器,和MapperScannerRegistrar的实现逻辑同样

  • org.mybatis.spring.SqlSessionTemplate:实现 SqlSession 和 DisposableBean 接口,SqlSession 操做模板实现类,承担 SqlSessionFactory 和 SqlSession 的职责

  • org.mybatis.spring.SqlSessionUtils:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对像

大体逻辑以下:

  1. 经过配置 MapperScannerConfigurer 的 Spring Bean,它会结合 ClassPathMapperScanner 扫描器,对指定包路径下的 Mapper 接口对应 BeanDefinition 对象(Spring Bean 的前身)进行修改,将其 Bean Class 修改成 MapperFactoryBean 类型,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean 对象,实现建立 Mapper 动态代理对象
  2. MapperFactoryBean 对象中getObject()中,根据 SqlSessionTemplate 对象为该 Mapper 接口建立一个动态代理对象,也就是说在咱们注入该 Mapper 接口时,实际注入的是 Mapper 接口对应的动态代理对象
  3. SqlSessionTemplate 对象中,承担 SqlSessionFactory 和 SqlSession 的职责,结合 Spring 的事务体系进行处理

配置示例

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
      init-method="init" destroy-method="close">
    <property name="url" value="${url}" />
    <property name="driverClassName" value="${driver}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
</bean>
<!-- spring和MyBatis完美整合,不须要mybatis的配置映射文件 -->
<bean id="mySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- bean的名称为sqlSessionFactory会出现错误 -->
    <property name="dataSource" ref="dataSource" />
    <!-- 引入配置文件 -->
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperLocations" value="classpath:com/fullmoon/study/mapping/*.xml" />
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.fullmoon.study.dao" />
    <property name="sqlSessionFactoryBeanName" value="mySqlSessionFactory" />
</bean>
  • 上面会建立DruidDataSource数据源,SqlSessionFactoryBeanMapperScannerConfigurer对象

SqlSessionFactoryBean

org.mybatis.spring.SqlSessionFactoryBean:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象

关于Spring的FactoryBean机制,不熟悉的先去了解一下,大体就是Spring在注入该类型的Bean时,调用的是它的getObject()方法

构造方法

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
  private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();

  /**
   * 指定的 mybatis-config.xml 路径的资源
   */
  private Resource configLocation;

  private Configuration configuration;

  /**
   * 指定 XML 映射文件路径的资源数组
   */
  private Resource[] mapperLocations;

  /**
   * 数据源
   */
  private DataSource dataSource;

  private TransactionFactory transactionFactory;

  private Properties configurationProperties;

  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

  /**
   * SqlSession 工厂,默认为 DefaultSqlSessionFactory
   */
  private SqlSessionFactory sqlSessionFactory;

  // EnvironmentAware requires spring 3.1
  private String environment = SqlSessionFactoryBean.class.getSimpleName();

  private boolean failFast;

  private Interceptor[] plugins;

  private TypeHandler<?>[] typeHandlers;

  private String typeHandlersPackage;

  @SuppressWarnings("rawtypes")
  private Class<? extends TypeHandler> defaultEnumTypeHandler;

  private Class<?>[] typeAliases;

  private String typeAliasesPackage;

  private Class<?> typeAliasesSuperType;

  private LanguageDriver[] scriptingLanguageDrivers;

  private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;

  // issue #19. No default provider.
  private DatabaseIdProvider databaseIdProvider;

  private Class<? extends VFS> vfs;

  private Cache cache;

  private ObjectFactory objectFactory;

  private ObjectWrapperFactory objectWrapperFactory;
}

能够看到上面定义的各类属性,这里就不一一解释了,根据名称能够知道属性的做用

afterPropertiesSet方法

afterPropertiesSet()方法,实现的 InitializingBean 接口,在 Spring 容器中,初始化该 Bean 时,会调用该方法,方法以下:

@Override
public void afterPropertiesSet() throws Exception {
    // 校验 dataSource 数据源不能为空
    notNull(dataSource, "Property 'dataSource' is required");
    // 校验 sqlSessionFactoryBuilder 构建器不能为空,上面默认 new 一个对象
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    // configuration 和 configLocation 有且只有一个不为空
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");

    // 初始化 SqlSessionFactory
    this.sqlSessionFactory = buildSqlSessionFactory();
}
  1. 校验 dataSource 数据源不能为空,因此配置该Bean时,必须配置一个数据源
  2. 校验 sqlSessionFactoryBuilder 构建器不能为空,上面默认 new 一个对象
  3. configuration 和 configLocation 有且只有一个不为空
  4. 调用buildSqlSessionFactory()方法,初始化 SqlSessionFactory

buildSqlSessionFactory方法

buildSqlSessionFactory()方法,根据配置信息构建一个SqlSessionFactory实例,方法以下:

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    // 初始化 Configuration 全局配置对象
    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      // 若是已存在 Configuration 对象
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      // 不然,若是配置了 mybatis-config.xml 配置文件
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      // 不然,建立一个 Configuration 对象
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    /*
     * 若是配置了 ObjectFactory(实例工厂)、ObjectWrapperFactory(ObjectWrapper工厂)、VFS(虚拟文件系统)
     * 则分别往 Configuration 全局配置对象设置
     */
    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

    /*
     * 若是配置了须要设置别名的包路径,则扫描该包路径下的 Class 对象
     * 往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表进行注册
     */
    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
        // 过滤掉匿名类
        .filter(clazz -> !clazz.isAnonymousClass())
        // 过滤掉接口
        .filter(clazz -> !clazz.isInterface())
        // 过滤掉内部类
        .filter(clazz -> !clazz.isMemberClass())
        .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    /*
     * 若是单独配置了须要设置别名的 Class 对象
     * 则将其往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表注册
     */
    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    // 往 Configuration 全局配置对象添加 Interceptor 插件
    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    // 扫描包路径,往 Configuration 全局配置对象添加 TypeHandler 类型处理器
    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    // 往 Configuration 全局配置对象添加 TypeHandler 类型处理器
    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    // 设置默认的枚举类型处理器
    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

    // 往 Configuration 全局配置对象添加 LanguageDriver 语言驱动
    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    // 设置默认的 LanguageDriver 语言驱动
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    // 设置当前数据源的数据库 id
    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    // 添加 Cache 缓存
    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        // 若是配置了 mybatis-config.xml 配置文件,则初始化 MyBatis
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    // 设置 Environment 环境信息
    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    // 若是配置了 XML 映射文件的路径
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        /*
         * 遍历全部的 XML 映射文件
         */
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            // 解析 XML 映射文件
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    // 经过构建器建立一个 DefaultSqlSessionFactory 对象
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

方法有点长,主要是经过配置信息建立一个 Configuration 对象,而后构建一个 DefaultSqlSessionFactory 对象

  1. 初始化 Configuration 全局配置对象

    1. 若是已存在 Configuration 对象,则直接使用该对象
    2. 不然,若是配置了 mybatis-config.xml 配置文件,则建立一个 XMLConfigBuilder 对象,待解析
    3. 不然,建立一个 Configuration 对象
  2. 往 Configuration 对象中设置相关配置属性

  3. 若是是1.2步生成的 Configuration 对象,那么调用 XMLConfigBuilderparse() 方法进行解析,初始化 MyBatis,在《MyBatis 初始化(一)之加载mybatis-config.xml》中分析过

  4. 若是配置了 XML 映射文件的路径mapperLocations,则进行遍历依次解析,经过建立XMLMapperBuilder对象,调用其parse()方法进行解析,在《MyBatis 初始化(二)之加载Mapper接口与映射文件》XMLMapperBuilder小节中分析过

  5. 经过SqlSessionFactoryBuilder构建器建立一个DefaultSqlSessionFactory对象

在 MyBatis-Spring 项目中,除了经过 mybatis-config.xml 配置文件配置 Mapper 接口的方式之外,还提供了几种配置方法,这里的SqlSessionFactoryBean.mapperLocations也算一种

这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称能够不一致,文件中里面配置 namepace 正确就能够了

getObject方法

getObject()方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 DefaultSqlSessionFactory 对象,方法以下:

@Override
public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      // 若是为空则初始化 sqlSessionFactory
      afterPropertiesSet();
    }

    // 返回 DefaultSqlSessionFactory 对象
    return this.sqlSessionFactory;
}

onApplicationEvent方法

onApplicationEvent(ApplicationEvent event)方法,监听 ContextRefreshedEvent 事件,若是还存在未初始化完成的 MapperStatement 们,则再进行解析,方法以下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    // 若是配置了须要快速失败,而且监听到了 Spring 容器初始化完成事件
    if (failFast && event instanceof ContextRefreshedEvent) {
      // fail-fast -> check all statements are completed
      // 将 MyBatis 中还未彻底解析的对象,在这里再进行解析
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
}

MapperFactoryBean

org.mybatis.spring.mapper.MapperFactoryBean:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象

构造方法

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

  /**
   * Mapper 接口
   */
  private Class<T> mapperInterface;

  /**
   * 是否添加到 {@link Configuration} 中,默认为 true
   */
  private boolean addToConfig = true;

  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
}
  • mapperInterface:对应的Mapper接口

  • addToConfig:是否添加到 Configuration 中,默认为 true

getObject方法

getObject()方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 Mapper 接口对应的动态代理对象,方法以下:

@Override
public T getObject() throws Exception {
    // getSqlSession() 方法返回 SqlSessionTemplate 对象
    return getSqlSession().getMapper(this.mapperInterface);
}

getSqlSession()方法在SqlSessionDaoSupport中定义,返回的是SqlSessionTemplate对象,后续会讲到

能够先暂时理解为就是返回一个 DefaultSqlSession,获取mapperInterfaceMapper接口对应的动态代理对象

这也就是为何在Spring中注入Mapper接口Bean时,咱们能够直接调用它的方法

checkDaoConfig方法

checkDaoConfig()方法,校验该 Mapper 接口是否被初始化并添加到 Configuration 中

@Override
protected void checkDaoConfig() {
    // 校验 sqlSessionTemplate 非空
    super.checkDaoConfig();

    // 校验 mapperInterface 非空
    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    /*
     * 若是该 Mapper 接口没有被解析至 Configuration,则对其进行解析
     */
    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        // 将该 Mapper 接口添加至 Configuration,会对该接口进行一系列的解析
        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();
      }
    }
}
  1. 校验 sqlSessionTemplate 非空
  2. 校验 mapperInterface 非空
  3. 若是该 Mapper 接口没有被解析至 Configuration,则对其进行解析

由于继承了DaoSupport抽象类,实现了 InitializingBean 接口,在 afterPropertiesSet() 方法中会调用checkDaoConfig()方法

SqlSessionDaoSupport

org.mybatis.spring.support.SqlSessionDaoSupport抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象,代码以下:

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSessionTemplate sqlSessionTemplate;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }

  @SuppressWarnings("WeakerAccess")
  protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
  }

  public final SqlSessionFactory getSqlSessionFactory() {
    return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
  }

  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSessionTemplate = sqlSessionTemplate;
  }

  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }

  public SqlSessionTemplate getSqlSessionTemplate() {
    return this.sqlSessionTemplate;
  }

  @Override
  protected void checkDaoConfig() {
    notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
  }

}
  • setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)方法,将 SqlSessionFactory 构建成咱们须要的 SqlSessionTemplate 对象,该对象在后续讲到
  • checkDaoConfig()方法,校验 SqlSessionTemplate 非空

MapperScannerConfigurer

org.mybatis.spring.mapper.MapperScannerConfigurer:实现了BeanDefinitionRegistryPostProcessor、InitializingBean接口,ApplicationContextAware、BeanNameAware接口

用于扫描Mapper接口,借助ClassPathMapperScanner修改Mapper接口的BeanDefinition对象(Spring Bean 的前身),将Bean的Class对象修改成MapperFactoryBean类型

那么在Spring初始化该Bean的时候,会初始化成MapperFactoryBean类型

构造方法

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

  /**
   * Mapper 接口的包路径
   */
  private String basePackage;

  /**
   * 是否要将接口添加到 Configuration 全局配置对象中
   */
  private boolean addToConfig = true;

  private String lazyInitialization;

  private SqlSessionFactory sqlSessionFactory;

  private SqlSessionTemplate sqlSessionTemplate;

  private String sqlSessionFactoryBeanName;

  private String sqlSessionTemplateBeanName;

  private Class<? extends Annotation> annotationClass;

  private Class<?> markerInterface;

  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;

  private ApplicationContext applicationContext;

  private String beanName;

  private boolean processPropertyPlaceHolders;

  private BeanNameGenerator nameGenerator;

  private String defaultScope;
  
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(this.basePackage, "Property 'basePackage' is required");
  }

}

SqlSessionFactoryBean小节的示例中能够看到,定义了basePackagesqlSessionFactoryBeanName两个属性

  • basePackage:Mapper 接口的包路径
  • addToConfig:是否要将接口添加到 Configuration 全局配置对象中
  • sqlSessionFactoryBeanName:SqlSessionFactory的Bean Name

afterPropertiesSet()方法中会校验basePackage非空

在 MyBatis-Spring 项目中,除了经过 mybatis-config.xml 配置文件配置 Mapper 接口的方式之外,还提供了几种配置方法,这里的配置 basePackage 属性也算一种

这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称能够不一致,文件中里面配置 namepace 正确就能够了

postProcessBeanDefinitionRegistry方法

postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,在 BeanDefinitionRegistry 完成后进行一些处理

这里会借助ClassPathMapperScanner扫描器,扫描指定包路径下的 Mapper 接口,方法以下:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      // 处理属性中的占位符
      processPropertyPlaceHolders();
    }

    // 建立一个 Bean 扫描器
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    // 是否要将 Mapper 接口添加到 Configuration 全局配置对象中
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    // 设置 SqlSessionFactory 的 BeanName
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    if (StringUtils.hasText(defaultScope)) {
      scanner.setDefaultScope(defaultScope);
    }
    // 添加几个过滤器
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
  1. 若是须要处理属性中的占位符,则调用processPropertyPlaceHolders()方法
  2. 建立一个 Bean 扫描器 ClassPathMapperScanner 对象
  3. 设置一些 Mapper 接口扫描器的属性,例如addToConfigsqlSessionFactoryBeanName
  4. 调用扫描器的registerFilters()方法,添加几个过滤器,过滤指定路径下的 Mapper 接口
  5. 调用其scan方法,开始扫描 basePackage路径下的 Mapper 接口

ClassPathMapperScanner

org.mybatis.spring.mapper.ClassPathMapperScanner:继承了ClassPathBeanDefinitionScanner抽象类

负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改成 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean 类型,实现建立 Mapper 动态代理对象

构造方法

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

  private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);

  /**
   * 是否要将 Mapper 接口添加到 Configuration 全局配置对象中
   */
  private boolean addToConfig = true;

  private boolean lazyInitialization;

  private SqlSessionFactory sqlSessionFactory;

  private SqlSessionTemplate sqlSessionTemplate;

  private String sqlSessionTemplateBeanName;

  /**
   * SqlSessionFactory Bean 的名称
   */
  private String sqlSessionFactoryBeanName;

  private Class<? extends Annotation> annotationClass;

  private Class<?> markerInterface;

  /**
   * 将 Mapper 接口转换成 MapperFactoryBean 对象
   */
  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

  private String defaultScope;
}
  • basePackage:Mapper 接口的包路径
  • addToConfig:是否要将接口添加到 Configuration 全局配置对象中
  • sqlSessionFactoryBeanName:SqlSessionFactory的Bean Name

上面这几个属性在 MapperScannerConfigurer 建立该对象的时候会进行赋值

registerFilters方法

registerFilters()方法,添加几个过滤器,用于扫描 Mapper 接口的过程当中过滤出咱们须要的 Mapper 接口,方法以下:

public void registerFilters() {
    // 标记是否接受全部的 Mapper 接口
    boolean acceptAllInterfaces = true;

    // if specified, use the given annotation and / or marker interface
    // 若是配置了注解,则扫描有该注解的 Mapper 接口
    if (this.annotationClass != null) {
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
      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
      // 若是上面两个都没有配置,则接受全部的 Mapper 接口
      addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
    }

    // exclude package-info.java
    // 排除 package-info.java 文件
    addExcludeFilter((metadataReader, metadataReaderFactory) -> {
      String className = metadataReader.getClassMetadata().getClassName();
      return className.endsWith("package-info");
    });
}
  1. 标记是否接受全部的接口
  2. 若是配置了注解,则添加一个过滤器,须要有该注解的接口
  3. 若是配置了某个接口,则添加一个过滤器,必须是该接口
  4. 若是没有第23步,则添加一个过滤器,接收全部的接口
  5. 添加过滤器,排除 package-info.java 文件

doScan方法

doScan(String... basePackages)方法,扫描指定包路径,根据上面的过滤器,获取路径下对应的 BeanDefinition 集合,进行一些后置处理,方法以下:

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 扫描指定包路径,根据上面的过滤器,获取到路径下 Class 对象的 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 {
      // 后置处理这些 BeanDefinition
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}
  1. 扫描指定包路径,根据上面的过滤器,获取到路径下符合条件的 Resource 资源,并生成对应的 BeanDefinition 对象注册到 BeanDefinitionRegistry 注册表中,并返回
  2. 若是不为空,则调用 processBeanDefinitions 方法,进行一些后置处理

processBeanDefinitions方法

processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions)方法,对指定包路径下符合条件的BeanDefinition 对象进行一些处理,修改其 Bean Class 为 MapperFactoryBean 类型,方法以下:

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    AbstractBeanDefinition definition;
    // <1> 获取 BeanDefinition 注册表,而后开始遍历
    BeanDefinitionRegistry registry = getRegistry();
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (AbstractBeanDefinition) holder.getBeanDefinition();
      boolean scopedProxy = false;
      if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
        // 获取被装饰的 BeanDefinition 对象
        definition = (AbstractBeanDefinition) Optional
            .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
            .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
                "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
        scopedProxy = true;
      }
      // <2> 获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      // <3> 往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的 Class 对象
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      /*
       * <4> 修改该 Mapper 接口的 Class对象 为 MapperFactoryBean.class
       * 这样一来当你注入该 Mapper 接口的时候,实际注入的是 MapperFactoryBean 对象,构造方法的入参就是 Mapper 接口
       */
      definition.setBeanClass(this.mapperFactoryBeanClass);

      // <5> 添加 addToConfig 属性
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      // <6> 开始添加 sqlSessionFactory 或者 sqlSessionTemplate 属性
      /*
       * 1. 若是设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象
       * 2. 不然,若是配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性
       */
      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;
      }

      /*
       * 1. 若是配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性
       * 2. 不然,若是配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性
       * SqlSessionFactory 和 SqlSessionTemplate 都配置了则会打印一个警告
       */
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          // 若是上面已经清楚的使用了 SqlSessionFactory,则打印一个警告
          LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        // 添加 sqlSessionTemplate 属性
        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;
      }

      /*
       * 上面没有找到对应的 SqlSessionFactory,则设置经过类型注入
       */
      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }

      definition.setLazyInit(lazyInitialization);

      if (scopedProxy) {
        // 已经封装过的则直接执行下一个
        continue;
      }

      if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
        definition.setScope(defaultScope);
      }

      /*
       * 若是不是单例模式,默认是
       * 将 BeanDefinition 在封装一层进行注册
       */
      if (!definition.isSingleton()) {
        BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
        if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
          registry.removeBeanDefinition(proxyHolder.getBeanName());
        }
        registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
      }

    }
}
  1. 获取 BeanDefinition 注册表,而后开始遍历

  2. 获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象

  3. 往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的名称,由于 MapperFactoryBean 的构造方法的入参就是 Mapper 接口

  4. 修改该 Mapper 接口的 Class 对象 为 MapperFactoryBean,根据第3步则会为该 Mapper 接口建立一个对应的 MapperFactoryBean 对象了

  5. 添加 addToConfig 属性,Mapper 是否添加到 Configuration 中

  6. 开始添加 sqlSessionFactory 或者 sqlSessionTemplate 属性

    1. 若是设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象,

      不然,若是配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性

      SqlSessionDaoSupportsetSqlSessionFactory(SqlSessionFactory sqlSessionFactory)方法中你会发现建立的就是SqlSessionTemplate 对象

    2. 若是配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性

      不然,若是配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性

    3. 上面没有找到对应的 SqlSessionFactory,则设置经过类型注入

该方法的处理逻辑大体如上描述,主要作了一下几个事:

  • 将 Mapper 接口的 BeanDefinition 对象的 beanClass 属性修改为了MapperFactoryBean的 Class 对象

  • 添加了一个入参为 Mapper 接口,这样初始化的 Spring Bean 就是该 Mapper 接口对应的 MapperFactoryBean 对象了

  • 添加MapperFactoryBean 对象的sqlSessionTemplate属性

@MapperScan注解

org.mybatis.spring.annotation.@MapperScan 注解,指定须要扫描的包,将包中符合条件的 Mapper 接口,注册成 beanClass 为 MapperFactoryBean 的 BeanDefinition 对象,从而实现建立 Mapper 对象

咱们代码以下:

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

  String[] value() default {};

  String[] basePackages() default {};

  Class<?>[] basePackageClasses() default {};

  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  Class<? extends Annotation> annotationClass() default Annotation.class;

  Class<?> markerInterface() default Class.class;

  String sqlSessionTemplateRef() default "";

  String sqlSessionFactoryRef() default "";


  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

  String lazyInitialization() default "";

  String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT;

}
  • valuebasePackage都是指定 Mapper 接口的包路径
  • @Import(MapperScannerRegistrar.class),该注解负责资源的导入,若是导入的是一个 Java 类,例如此处为 MapperScannerRegistrar 类,Spring 会将其注册成一个 Bean 对象

MapperScannerRegistrar

org.mybatis.spring.annotation.MapperScannerRegistrar:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口

做为@MapperScann 注解的注册器,根据注解信息注册一个 MapperScannerConfigurer 对象,用于扫描 Mapper 接口

registerBeanDefinitions方法

registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 得到 @MapperScan 注解信息
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
        // 生成 Bean 的名称,'org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0'
        generateBaseBeanName(importingClassMetadata, 0));
    }
}
  1. 得到 @MapperScan 注解信息
  2. 调用generateBaseBeanName方法,为MapperScannerConfigurer生成一个beanName:org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0
  3. 调用registerBeanDefinitions重载方法,注册一个类型为MapperScannerConfigurer的 BeanDefinition 对象

registerBeanDefinitions重载方法

registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName)方法

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
  BeanDefinitionRegistry registry, String beanName) {
    // 建立一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    // 添加是否处理属性中的占位符属性
    builder.addPropertyValue("processPropertyPlaceHolders", true);

    /*
     * 依次添加注解中的配置属性
     */
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }

    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }

    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }

    /*
     * 获取到配置的 Mapper 接口的包路径
     */
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));

    /*
     * 若是没有 Mapper 接口的包路径,则默认使用注解类所在的包路径
     */
    if (basePackages.isEmpty()) {
      basePackages.add(getDefaultBasePackage(annoMeta));
    }

    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }

    String defaultScope = annoAttrs.getString("defaultScope");
    if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
      builder.addPropertyValue("defaultScope", defaultScope);
    }

    // 添加 Mapper 接口的包路径属性
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

    // 往 BeanDefinitionRegistry 注册表注册 MapperScannerConfigurer 对应的 BeanDefinition 对象
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

}
  1. 建立一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象
  2. 添加是否处理属性中的占位符属性processPropertyPlaceHolders
  3. 依次添加@MapperScan注解中的配置属性,例如:sqlSessionFactoryBeanNamebasePackages
  4. 往 BeanDefinitionRegistry 注册表注册 MapperScannerConfigurer 类型的 BeanDefinition 对象

这样在 Spring 容器初始化的过程当中,会建立一个 MapperScannerConfigurer 对象,而后回到MapperScannerConfigurerpostProcessBeanDefinitionRegistry方法中,对包路径下的 Mapper 接口进行解析,前面已经分析过了

RepeatingRegistrar内部类

MapperScannerRegistrar的内部类,代码以下:

static class RepeatingRegistrar extends MapperScannerRegistrar {
    /**
     * {@inheritDoc}
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      AnnotationAttributes mapperScansAttrs = AnnotationAttributes
          .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
      if (mapperScansAttrs != null) {
        // 获取 MapperScan 注解数组
        AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");
        /*
         * 依次处理每一个 MapperScan 注解
         */
        for (int i = 0; i < annotations.length; i++) {
          registerBeanDefinitions(importingClassMetadata, annotations[i], registry, generateBaseBeanName(importingClassMetadata, i));
        }
      }
    }
}
  • 能够回到@MapperScan注解上面的@Repeatable(MapperScans.class)信息,能够看到若是同一个类上面定义多个@MapperScan注解,则会生成对应的@MapperScans注解
  • RepeatingRegistrar 用于处理@MapperScans注解,依次处理@MapperScan注解的信息
  • 和 MapperScannerRegistrar 同样的处理方式,不过生成的多个 MapperScannerConfigurer 对应的 beanName 的后缀不同

自定义 <mybatis:scan /> 标签

除了配置 MapperScannerConfigurer 对象和经过 @MapperScan 注解扫描 Mapper 接口之外,咱们还能够经过 MyBatis 提供的 scan 标签来扫描 Mapper 接口

示例

<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />

spring.schemas

META-INF/spring.schemas 定义以下:

http\://mybatis.org/schema/mybatis-spring-1.2.xsd=org/mybatis/spring/config/mybatis-spring.xsd
http\://mybatis.org/schema/mybatis-spring.xsd=org/mybatis/spring/config/mybatis-spring.xsd
  • xmlns 为 http://mybatis.org/schema/mybatis-spring-1.2.xsdhttp://mybatis.org/schema/mybatis-spring.xsd
  • xsd 为 mybatis-spring.xsd

spring.handler

META-INF/spring.handlers 定义以下:

http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
  • 定义了 MyBatis 的 XML Namespace 的处理器 NamespaceHandler 对象

NamespaceHandler

org.mybatis.spring.config.NamespaceHandler:继承 NamespaceHandlerSupport 抽象类,MyBatis 的 XML Namespace 的处理器,代码以下:

public class NamespaceHandler extends NamespaceHandlerSupport {

  /**
   * {@inheritDoc}
   */
  @Override
  public void init() {
    registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());
  }

}
  • <mybatis:scan /> 标签,使用 MapperScannerBeanDefinitionParser 解析

MapperScannerBeanDefinitionParser

org.mybatis.spring.config.MapperScannerBeanDefinitionParser:实现 BeanDefinitionParser 接口,<mybatis:scan /> 的解析器,代码以下:

public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionParser {

  private static final String ATTRIBUTE_BASE_PACKAGE = "base-package";
  private static final String ATTRIBUTE_ANNOTATION = "annotation";
  private static final String ATTRIBUTE_MARKER_INTERFACE = "marker-interface";
  private static final String ATTRIBUTE_NAME_GENERATOR = "name-generator";
  private static final String ATTRIBUTE_TEMPLATE_REF = "template-ref";
  private static final String ATTRIBUTE_FACTORY_REF = "factory-ref";
  private static final String ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS = "mapper-factory-bean-class";
  private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization";
  private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope";

  /**
   * 这个方法和 {@link MapperScannerRegistrar} 同样的做用
   * 解析 <mybatis:scan /> 标签中的配置信息,设置到 MapperScannerConfigurer 的 BeanDefinition 对象中
   * {@inheritDoc}
   *
   * @since 2.0.2
   */
  @Override
  protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    // 建立一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

    ClassLoader classLoader = ClassUtils.getDefaultClassLoader();

    // 添加是否处理属性中的占位符属性
    builder.addPropertyValue("processPropertyPlaceHolders", true);
    /*
     * 解析 `scan` 标签中的配置,添加到 BeanDefinition 中
     */
    try {
      String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);
      if (StringUtils.hasText(annotationClassName)) {
        @SuppressWarnings("unchecked")
        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) classLoader
            .loadClass(annotationClassName);
        builder.addPropertyValue("annotationClass", annotationClass);
      }
      String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);
      if (StringUtils.hasText(markerInterfaceClassName)) {
        Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);
        builder.addPropertyValue("markerInterface", markerInterface);
      }
      String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);
      if (StringUtils.hasText(nameGeneratorClassName)) {
        Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
        BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);
        builder.addPropertyValue("nameGenerator", nameGenerator);
      }
      String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS);
      if (StringUtils.hasText(mapperFactoryBeanClassName)) {
        @SuppressWarnings("unchecked")
        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = (Class<? extends MapperFactoryBean>) classLoader
            .loadClass(mapperFactoryBeanClassName);
        builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
      }
    } catch (Exception ex) {
      XmlReaderContext readerContext = parserContext.getReaderContext();
      readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
    }

    builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));
    builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));
    builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));
    builder.addPropertyValue("defaultScope", element.getAttribute(ATTRIBUTE_DEFAULT_SCOPE));
    builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));

    return builder.getBeanDefinition();
  }

  /**
   * {@inheritDoc}
   *
   * @since 2.0.2
   */
  @Override
  protected boolean shouldGenerateIdAsFallback() {
    return true;
  }

}
  • 代码的实现逻辑MapperScannerRegistrar一致,建立MapperScannerConfigurer对应的BeanDefinition对象,而后去解析<mybatis:scan /> 标签中的配置信息

SqlSessionTemplate

org.mybatis.spring.SqlSessionTemplate:实现 SqlSession 和 DisposableBean 接口,SqlSession 操做模板实现类

实际上,代码实现和 org.apache.ibatis.session.SqlSessionManager 类似,承担 SqlSessionFactory 和 SqlSession 的职责

构造方法

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  /**
   * a factory of SqlSession
   */
  private final SqlSessionFactory sqlSessionFactory;

  /**
   * {@link Configuration} 中默认的 Executor 执行器类型,默认 SIMPLE
   */
  private final ExecutorType executorType;

  /**
   * SqlSessionInterceptor 代理对象
   */
  private final SqlSession sqlSessionProxy;

  /**
   * 异常转换器,MyBatisExceptionTranslator 对象
   */
  private final PersistenceExceptionTranslator exceptionTranslator;

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  }

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
  }

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // 建立一个 SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
  }
}
  • sqlSessionFactory:用于建立 SqlSession 对象
  • executorType:执行器类型,建立 SqlSession 对象时根据它建立对应的 Executor 执行器,默认为
  • sqlSessionProxy:SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
  • exceptionTranslator:异常转换器

在调用SqlSessionTemplate中的SqlSession相关方法时,内部都是直接调用sqlSessionProxy动态代理对象的方法,咱们来看看是如何处理的

SqlSessionInterceptor

SqlSessionTemplate的内部类,实现了 InvocationHandler 接口,做为sqlSessionProxy动态代理对象的代理类,对 SqlSession 的相关方法进行加强

代码以下:

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // <1> 获取一个 SqlSession 对象
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 执行 SqlSession 的方法
        Object result = method.invoke(sqlSession, args);
        // 当前 SqlSession 不处于 Spring 托管的事务中
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          // 强制提交
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          // 关闭 SqlSession 会话,释放资源
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          // 对异常进行转换,差很少就是转换成 MyBatis 的异常
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator
              .translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
}
  1. 调用SqlSessionUtilsgetSqlSession方法,获取一个 SqlSession 对象
  2. 执行 SqlSession 的方法
  3. 当前 SqlSession 不处于 Spring 托管的事务中,则强制提交
  4. 调用SqlSessionUtilscloseSqlSession方法,“关闭”SqlSession 对象,这里的关闭不是真正的关闭

SqlSessionHolder

org.mybatis.spring.SqlSessionHolder:继承 org.springframework.transaction.support.ResourceHolderSupport 抽象类,SqlSession 持有器,用于保存当前 SqlSession 对象,保存到 org.springframework.transaction.support.TransactionSynchronizationManager 中,代码以下:

public final class SqlSessionHolder extends ResourceHolderSupport {

  /**
   * SqlSession 对象
   */
  private final SqlSession sqlSession;

  /**
   * 执行器类型
   */
  private final ExecutorType executorType;

  /**
   * PersistenceExceptionTranslator 对象
   */
  private final PersistenceExceptionTranslator exceptionTranslator;

  public SqlSessionHolder(SqlSession sqlSession, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSession, "SqlSession must not be null");
    notNull(executorType, "ExecutorType must not be null");

    this.sqlSession = sqlSession;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
  }

}
  • 当存储到 TransactionSynchronizationManager 中时,使用的 KEY 为建立该 SqlSession 对象的 SqlSessionFactory 对象,后续会分析

SqlSessionUtils

org.mybatis.spring.SqlSessionUtils:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对象

getSqlSession方法

getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)方法,注释以下:

Gets an SqlSession from Spring Transaction Manager or creates a new one if needed. Tries to get a SqlSession out of current transaction.
If there is not any, it creates a new one. Then, it synchronizes the SqlSession with the transaction if Spring TX is active and SpringManagedTransactionFactory is configured as a transaction manager.

从事务管理器(线程安全)中获取一个 SqlSession 对象,若是不存在则建立一个 SqlSession,而后注册到事务管理器中,方法以下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
  PersistenceExceptionTranslator exceptionTranslator) {
    
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    // 从 Spring 事务管理器中获取一个 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    // 获取到 SqlSession 对象
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    // 上面没有获取到,则建立一个 SqlSession
    session = sessionFactory.openSession(executorType);

    // 将上面建立的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
}
  1. 从 Spring 事务管理器中,根据 SqlSessionFactory 获取一个 SqlSessionHolder 对象

  2. 调用 sessionHolder 方法,获取到 SqlSession 对象,方法以下

    private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
        SqlSession session = null;
        if (holder != null && holder.isSynchronizedWithTransaction()) {
          // 若是执行器类型发生了变动,抛出 TransientDataAccessResourceException 异常
          if (holder.getExecutorType() != executorType) {
            throw new TransientDataAccessResourceException(
                "Cannot change the ExecutorType when there is an existing transaction");
          }
    
          // 增长计数,关闭 SqlSession 时使用
          holder.requested();
    
          LOGGER.debug(() -> "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
          // 得到 SqlSession 对象
          session = holder.getSqlSession();
        }
        return session;
    }
  3. 若是 SqlSession 对象不为 null,则直接返回,接下来会建立一个

  4. 上面没有获取到,则建立一个 SqlSession 对象

  5. 调用 registerSessionHolder 方法,将上面建立的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册

  6. 返回新建立的 SqlSession 对象

registerSessionHolder方法

registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session)方法

注释以下:

Register session holder if synchronization is active (i.e. a Spring TX is active).

Note: The DataSource used by the Environment should be synchronized with the transaction either through DataSourceTxMgr or another tx synchronization.

Further assume that if an exception is thrown, whatever started the transaction will handle closing / rolling back the Connection associated with the SqlSession.

若是事务管理器处于激活状态,则将 SqlSession 封装成 SqlSessionHolder 对象注册到其中,方法以下:

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
  PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
    SqlSessionHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();

      // <1> 若是使用 Spring 事务管理器
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

        // <1.1> 建立 SqlSessionHolder 对象
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        // <1.2> 绑定到 TransactionSynchronizationManager 中
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        // <1.3> 建立 SqlSessionSynchronization 到 TransactionSynchronizationManager 中
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        // <1.4> 设置同步
        holder.setSynchronizedWithTransaction(true);
        // <1.5> 增长计数
        holder.requested();
      } else {
        // <2> 若是非 Spring 事务管理器,抛出 TransientDataAccessResourceException 异常
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
          LOGGER.debug(() -> "SqlSession [" + session
              + "] was not registered for synchronization because DataSource is not transactional");
        } else {
          throw new TransientDataAccessResourceException(
              "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else {
      LOGGER.debug(() -> "SqlSession [" + session
          + "] was not registered for synchronization because synchronization is not active");
    }
}
  1. 若是使用 Spring 事务管理器,才会进行注册
  2. 建立 SqlSessionHolder 对象holder
  3. 绑定到 TransactionSynchronizationManager 中,key 为 SqlSessionFactory 对象
  4. 建立 SqlSessionSynchronization 对象(事务同步器)到 TransactionSynchronizationManager 中
  5. 设置 holdersynchronizedWithTransaction 属性为ture,和事务绑定了
  6. 增长 holderreferenceCount 引用数量

closeSqlSession方法

closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)方法,注释以下:

Checks if SqlSession passed as an argument is managed by Spring TransactionSynchronizationManager

If it is not, it closes it, otherwise it just updates the reference counter and lets Spring call the close callback when the managed transaction ends

若是 SqlSessionFactory 是由 Spring 的事务管理器管理,而且和入参中的 session 相同,那么只进行释放,也就是将 referenceCount 引用数量减一,不然就直接关闭了

方法以下:

public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    // <1> 从 TransactionSynchronizationManager 中,得到 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    // <2.1> 若是相等,说明在 Spring 托管的事务中,则释放 holder 计数
    if ((holder != null) && (holder.getSqlSession() == session)) {
      LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
      holder.released();
    } else {
      // <2.2> 若是不相等,说明不在 Spring 托管的事务中,直接关闭 SqlSession 对象
      LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
      session.close();
    }
}
  1. 从事务管理器中,根据 SqlSessionFactory 得到 SqlSessionHolder 对象
  2. 若是相等,说明在 Spring 托管的事务中,则释放 holder 计数
  3. 不然,不在 Spring 托管的事务中,直接关闭 SqlSession 对象

isSqlSessionTransactional方法

isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory)方法,判断 SqlSession 对象是否被 Sping 的事务管理器管理,代码以下:

public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    // 从 TransactionSynchronizationManager 中,得到 SqlSessionHolder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    // 若是相等,说明在 Spring 托管的事务中
    return (holder != null) && (holder.getSqlSession() == session);
}

SqlSessionSynchronization

org.mybatis.spring.SqlSessionUtils的内部类,继承了 TransactionSynchronizationAdapter 抽象类,SqlSession 的事务同步器,基于 Spring Transaction 体系

注释以下:

Callback for cleaning up resources.

It cleans TransactionSynchronizationManager and also commits and closes the SqlSession.

It assumes that Connection life cycle will be managed by DataSourceTransactionManager or JtaTransactionManager

回调的时候清理资源

构造方法

private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {

    private final SqlSessionHolder holder;

    private final SqlSessionFactory sessionFactory;

    /**
     * 是否开启
     */
    private boolean holderActive = true;

    public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
      notNull(holder, "Parameter 'holder' must be not null");
      notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");

      this.holder = holder;
      this.sessionFactory = sessionFactory;
    }
}

getOrder方法

@Override
public int getOrder() {
  // order right before any Connection synchronization
  return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
}

suspend方法

当事务挂起时,取消当前线程的绑定的 SqlSessionHolder 对象,方法以下:

@Override
public void suspend() {
  if (this.holderActive) {
    LOGGER.debug(() -> "Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
    TransactionSynchronizationManager.unbindResource(this.sessionFactory);
  }
}

resume方法

当事务恢复时,从新绑定当前线程的 SqlSessionHolder 对象,方法以下:

@Override
public void resume() {
  if (this.holderActive) {
    LOGGER.debug(() -> "Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
    TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
  }
}

beforeCommit方法

在事务提交以前,调用 SqlSession#commit() 方法以前,提交事务。虽说,Spring 自身也会调用 Connection#commit() 方法,进行事务的提交。可是,SqlSession#commit() 方法中,不只仅有事务的提交,还有提交批量操做,刷新本地缓存等等,方法以下:

@Override
public void beforeCommit(boolean readOnly) {
  // Connection commit or rollback will be handled by ConnectionSynchronization or DataSourceTransactionManager.
  // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so they are actually executed.
  // SpringManagedTransaction will no-op the commit over the jdbc connection
  // TODO This updates 2nd level caches but the tx may be rolledback later on!
  if (TransactionSynchronizationManager.isActualTransactionActive()) {
    try {
      LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
      // 提交事务
      this.holder.getSqlSession().commit();
    } catch (PersistenceException p) {
      // 若是发生异常,则进行转换,并抛出异常
      if (this.holder.getPersistenceExceptionTranslator() != null) {
        DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
            .translateExceptionIfPossible(p);
        if (translated != null) {
          throw translated;
        }
      }
      throw p;
    }
  }
}

beforeCompletion方法

提交事务完成以前,关闭 SqlSession 对象,在 beforeCommit 以后调用,方法以下:

@Override
public void beforeCompletion() {
  // Issue #18 Close SqlSession and deregister it now
  // because afterCompletion may be called from a different thread
  if (!this.holder.isOpen()) {
    LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
    // 取消当前线程的绑定的 SqlSessionHolder 对象
    TransactionSynchronizationManager.unbindResource(sessionFactory);
    // 标记无效
    this.holderActive = false;
    LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
    // 关闭 SqlSession 对象
    this.holder.getSqlSession().close();
  }
}

afterCompletion方法

在事务完成以后,关闭 SqlSession 对象,解决可能出现的跨线程的状况,方法以下:

@Override
public void afterCompletion(int status) {
  if (this.holderActive) { // 处于有效状态
    // afterCompletion may have been called from a different thread
    // so avoid failing if there is nothing in this one
    LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
    // 取消当前线程的绑定的 SqlSessionHolder 对象
    TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
    // 标记无效
    this.holderActive = false;
    LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
    // 关闭 SqlSession 对象
    this.holder.getSqlSession().close();
  }
  this.holder.reset();
}

总结

还有一部份内容在org.mybatis.spring.batch包路径下,基于 Spring Batch 框架,Spring 和 MyBatis 的批处理进行集成,感兴趣的小伙伴能够去阅读一下

我一般都是经过配置示例中方式配置MyBatis的,由于我以为配置文件易于维护,比较可观,固然也经过注解(@MapperScan)的方式进行配置,原理相同

  1. 首先配置DataSource数据源的 Sping Bean,咱们一般不会使用 MyBatis 自带的数据源,由于其性能很差,都是经过Druid或者HikariCP等第三方组件来实现

  2. 配置SqlSessionFactoryBean的 Spring Bean,设置数据源属性dataSource,还能够配置configLocation(mybatis-config.xml配置文件的路径)、mapperLocations(XML映射文件的路径)等属性,这样让 Spring 和 MyBatis 完美的整合到一块儿了

  3. 配置MapperScannerConfigurer的 Spring Bean,设置basePackage(须要扫描的Mapper接口的路径)、sqlSessionFactoryBeanName(上面定义的SqlSessionFactoryBean)等属性

    由于实现了 BeanDefinitionRegistryPostProcessor 接口,在这些 Mapper 接口的 BeanDefinition 对象(Spring Bean 的前身)注册完毕后,能够进行一些处理

    在这里会修改这些 BeanDefinition 对象为 MapperFactoryBean 类型,在初始化 Spring Bean 的过程当中则建立的是 MapperFactoryBean 对象,注入该对象则会调用其 getObject() 方法,返回的该 Mapper 接口对应的动态代理对象

    这样当你注入 Mapper 接口时,实际注入的是其动态代理对象

  4. SqlSessionTemplate对象中,承担 SqlSessionFactory 和 SqlSession 的职责

到这里,相信你们对 MyBatis 集成到 Spring 的方案有了必定的了解,感谢你们的阅读!!!😄😄😄

参考文章:芋道源码《精尽 MyBatis 源码分析》

相关文章
相关标签/搜索