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

MyBatis 版本:3.5.2

MyBatis-Spring 版本:2.0.3

MyBatis-Spring-Boot-Starter 版本:2.1.4

在前面的一系列文档中对整个 MyBatis 框架进行了分析,相信你对 MyBatis 有了一个更加深入的了解。在使用它的过程中,需要自己创建 SqlSessionFactory 和 SqlSession,然后获取到 Mapper 接口的动态代理对象,执行数据库相关操作,对这些对象的管理并不是那么简单。我们通常会结合 Spring 来使用 MyBatis,将这些对象作为 Spring Bean 注入到 Spring 容器,也允许参与到 Spring 的事务管理之中

Spring 官方并没有提供对 MyBatis3 的集成方案,于是在 MyBatis 社区将对 Spring 的集成作为一个 MyBatis 子项目 MyBatis-Spring,帮助你将 MyBatis 代码无缝地整合到 Spring 中,那么我们一起来看看这个子项目是如何集成到 Spring 中的

在开始读这篇文档之前,需要对 Spring 有一定的了解,可以结合我的源码注释(Mybatis-Spring 源码分析 GitHub 地址)进行阅读,MyBatis-Spring官方文档

简述

主要涉及到的几个类:

  • 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 的方案有了一定的了解,感谢大家的阅读!!!

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/lifullmoon/p/14015235.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程