(五)Mybatis持久化框架原理之MapperFactoryBean方式和Spring集成

本文深入探讨Mybatis与Spring框架的集成过程,包括pom文件配置、Spring配置文件设置及Java测试类编写。分析了集成中的关键类SqlSessionFactoryBean、MapperFactoryBean的工作机制,以及SqlSessionTemplate的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、Mybatis和Spring集成项目准备

1.pom文件改动

2.Spring配置文件

3.Java测试类

二、UML图和流程分析

1.UML类图

2.流程分析

三、源码分析

1.SqlSessionFactoryBean配置

2.MapperFactoryBean配置

2.1 DaoSupport类

2.2 SqlSessionDaoSupport

3.SqlSessionTemplate


一、Mybatis和Spring集成项目准备

在第一篇配置的基础上增加扩展基础的Spring集成,Mybatis基础配置篇见此(一)Mybatis持久化框架原理之项目搭建。同时,要想看懂这篇mybatis-spring的集成原理,首先需要搞懂mybatis核心的处理过程,具体原理分析见下列几篇源码解读:

1.pom文件改动

<properties>
    <!-- mybatis版本号 -->
    <mybatis.version>3.4.1</mybatis.version>
    <!-- spring 版本 -->
    <spring.version>4.3.5.RELEASE</spring.version>
    <!-- mybatis spring 连接包 -->
    <mybatis.spring.version>2.0.2</mybatis.spring.version>
    <!-- junit 版本 -->
    <junit.version>4.12</junit.version>
</properties>

<dependencies>
    <!-- mybatis核心包 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>

    <!-- spring 核心包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- mybatis spring 连接包 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatis.spring.version}</version>
    </dependency>

    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
    </dependency>

    <!-- 测试junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
    </dependency>
</dependencies>

2.Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 引入properties文件 -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 定义数据源 -->
    <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource">
        <property name="driverType" value="${connection.driver}"/>
        <property name="URL" value="${connection.url}"/>
        <property name="user" value="${connection.username}"/>
        <property name="password" value="${connection.password}"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- mybatis 配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 指定实体类映射文件,可以同时指定某一包以及子包下面的所有配置文件,
        mapperLocations和configLocation有一个即可,当需要为实体类指定别名时,
        可指定configLocation属性,再在mybatis总配置文件中采用mapper引入实体类映射文件 -->
        <property  name="mapperLocations"  value="classpath*:*Mapper.xml"/>
    </bean>

    <!-- 指定需要引用的接口和sqlSessionFactory对象 -->
    <bean id="demoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.iboxpay.mapper.DemoMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

3.Java测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestMybatisSpringTest {

    @Autowired
    private DemoMapper demoMapper;

    @Test
    public void testSpringMybatis() {
        System.out.println(demoMapper.select("1", 1, 's'));
    }

}
/*
打印结果:
[DemoEntity(sno=516429, name=test, sex=男, birth=Tue May 19 11:05:16 CST 2020, grade=2)]
*/

二、UML图和流程分析

1.UML类图

下图是此方法的UML类图结构:

上面的UML类图涉及了Spring的接口使用,有兴趣可以去翻看一系列的Spring接口。

由上面的UML类图可以看出来,mybatis-spring集成包最重要的为四个类,但基于上面的实例我们此次只分析以下三个类,MapperScannerConfigurer类不在此例子中做分析:

  1. SqlSessionFactoryBean:实现了Spring的FactoryBean接口类,将其变成和Spring相关联的SqlSession的工厂类。
  2. SqlSessionTemplate:Mybatis中SqlSession的实现类,内部实际上封装了SqlSessionFactory和SqlSession,经常看到,但是在本例子中没有使用;
  3. MapperFactoryBean:类似于SqlSessionFactoryBean,只是该类针对的是Mapper接口形成的FactoryBean,里面封装了mapperInterface和SqlSessionTemplate,以便于和传统Mybatis一样从SqlSession中拿取Mapper代理类,进而操作数据库。

其中,mybatis-spring包集成spring采用的是实现spring的通用接口,后续再说一下此集成过程中源码流程。

2.流程分析

流程如下:

大致流程如图中所述,都是mybatis利用Spring的Bean初始化机制来集成到Spring中的,接下来我们看看其具体的实现细节。

三、源码分析

1.SqlSessionFactoryBean配置

该类是Spring上下文创建SqlSessionFactory的FactoryBean,并在依赖注入时将SqlSession-Factory传递给Mybatis的mapper代理工厂。首先看到其实现的三个接口:

  1. FactoryBean:Spring中有BeanFactory也有FactoryBean,很容易把这个搞混,BeanFactory指的是Spring工厂的最高接口,而FactoryBean即是为某一类Bean创建一个工厂,这个工厂只产生这一类的Bean。SqlSessionFactoryBean实现的接口是FactoryBean<SqlSessionFactory>,指定了SqlSessionFactory,因此这个Bean只会产生SqlSessionFactory;
  2. InitializingBean:实现该接口的类,将在类的成员属性被spring框架初始化之后调用其中唯一的接口方法afterPropertiesSet;
  3. ApplicationListener:spring的发布/订阅模式(也叫观察者模式),当ApplicationEvent被发布的时候,将会调用ApplicationListener的实现类,SqlSessionFactoryBean的事件触发为ContextRefreshedEvent,当spring容器初始化之后将会调用此方法。

SqlSessionFactoryBean该类关键源码如下:

public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, 
    ApplicationListener<ApplicationEvent> {
  // config.xml文件对应的资源对象
  private Resource configLocation;
  // mybatis最重要的配置对象,里面有mybatis运行时所需要的所有数据配置
  private Configuration configuration;
  // mapper.xml文件对应的资源对象数组
  private Resource[] mapperLocations;
  // 数据源
  private DataSource dataSource;
  // 熟悉的SqlSessionFactory构造器
  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = 
          new SqlSessionFactoryBuilder();
  // SqlSessionFactory获取SqlSession的工厂
  private SqlSessionFactory sqlSessionFactory;
  // 其它的成员对象都忽略,都是在Configuration中可以找到的
  // 暂时看到这几个重要的对象,同时中间忽略这些成员对象的setter和getter方法
  @Override
  public void afterPropertiesSet() throws Exception {
    // 刚刚说过,当这个Bean初始化之后Spring工厂将会调用这个方法
    // 忽略判断数据源dataSource和sqlSessionFactoryBuilder为空语句
    // 这里也会判断configuration和configLocation这两个对象不能同时存在,否则
    // 否则将会报错
    // 确定必要对象的值不为空后,进入构造sqlSessionFactory方法
    this.sqlSessionFactory = buildSqlSessionFactory();
  }
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    // 这个方法比较长,但总体而言和原生mybatis解析config.xml文件流程还是差不多的
    final Configuration targetConfiguration;
    // 熟悉的解析config.xml文件的XMLConfigBuilder构造器
    XMLConfigBuilder xmlConfigBuilder = null;
    // 如果configuration对象不为空
    if (this.configuration != null) {
      // 直接将configuration赋值给targetConfiguration变量
      // configuration不为空则意味着至少config.xml文件那些流程已经解析过了
      targetConfiguration = this.configuration;
      // 为variables属性再赋值
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables()
                .putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      // 如果configuration为空且configLocation配置了config.xml文件位置
      // 则实例化XMLConfigBuilder构造器,注意这里只是实例化了XMLConfigBuilder
      // 还没有调用parse方法,因此config.xml文件还没有被解析
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation
              .getInputStream(), null, this.configurationProperties);
      // 将里面实例化的configuration赋值给targetConfiguration
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      // 如果configLocation和configuration都为空,则创建一个默认的
      // Configuration对象
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties)
              .ifPresent(targetConfiguration::setVariables);
    }
    // 设置setObjectFactory、setObjectWrapperFactory和setVfsImpl对象,略
    ...
    // 别名包属性不为空
    if (hasLength(this.typeAliasesPackage)) {
      // 如果不为空则获取包下的class类
      // 再将其注册进别名注册中心typeAliasRegistry
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType)
          .stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass())
          .forEach(targetConfiguration
                  .getTypeAliasRegistry()::registerAlias);
    }
    // 如果显示的配置了别名类,则直接将这些类配置到别名注册中心typeAliasRegistry
    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry()
                .registerAlias(typeAlias);
      });
    }
    // 如果插件不为空的话,则设置添加插件到configuration对象中
    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
      });
    }
    // 类似别名一样,如果设置了TypeHandler类型处理器包,则搜索出来再注册
    // 到typeHandlerRegistr注册中心
    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream()
          .filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .filter(clazz -> 
                  ClassUtils.getConstructorIfAvailable(clazz) != null)
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }
    // 类似别名
    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry()
                .register(typeHandler);
      });
    }
    // 配置语言驱动器languageDriver
    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry()
                .register(languageDriver);
      });
    }
    // 如果手动配置了defaultDriverClass则将手动配置的值赋值给configuration
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);
    // 设置databaseIdProvider 
    if (this.databaseIdProvider != null) {
      try {
        targetConfiguration.setDatabaseId(
                this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException();
      }
    }
    // 如果配置了cache缓存对象则添加到configuration对象中
    Optional.ofNullable(this.cache)
            .ifPresent(targetConfiguration::addCache);
    // 如果解析config.xml文件的构造器不为空(即配置了config.xml文件)
    if (xmlConfigBuilder != null) {
      try {
        // 调用构造器的parse,执行解析config.xml文件流程
        xmlConfigBuilder.parse();
      } catch (Exception ex) {
        throw new NestedIOException();
      } finally {
        ErrorContext.instance().reset();
      }
    }
    // 如果手动配置了mapperLocations(即配置了mapper.xml文件位置)
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        // 为空不进行操作,只会打印debug日志告诉mapperLocations为空
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          // 过滤为null的资源对象
          if (mapperLocation == null) {
            continue;
          }
          try {
            // 和解析<mapper/>标签引入的mapper.xml文件一样
            // 使用XMLMapperBuilder构造器解析mapper.xml文件
            XMLMapperBuilder xmlMapperBuilder = 
                    new XMLMapperBuilder(mapperLocation.getInputStream(),
                    targetConfiguration, mapperLocation.toString(), 
                    targetConfiguration.getSqlFragments());
            // 这里需要注意的是,如果统一个mapper在config.xml中被引入了
            // 在mapperLocations对象也指定了,会导致mapper.xml文件被解析两次
            // 添加ResultMap时判断存在重复值将会抛出异常
            // 即要么采用在config.xml中配置mapper.xml文件,要么采用
            // 只配置mapperLocations对象值,两者混合用容易出错
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException();
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    }
    // 前面的流程已经解析完,可以把这个方法分为两个流程,第一个流程则是添加
    // 在Spring配置中配置的属性值,第二个流程则是接入原生mybatis配置方式
    // 在平常的使用中,原生的mybatis配置方式基本不适用,而是全部使用Spring
    // 方式管理配置,因此建议不配置config.xml文件,直接配置mapperLocations
    // 这些属性,可以更直观的看到自己的配置;
    // 像原生Mybatis一样,解析完XML文件则使用configuration对象构造
    // SqlSessionFactory对象,默认是DefaultSqlSessionFactory类型
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }
  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    if (failFast && event instanceof ContextRefreshedEvent) {
      // 当上下文刷新完毕后,将会再次加载mybatis前一次失败的配置
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
  }
}

到此,我们已经达到了原生mybatis使用时获得加载完所有信息的sqlSessionFactory工厂类,接下来看一下该集成方式的下一步。

2.MapperFactoryBean配置

我们使用原生的Mybatis时都要从sqlSessionFactory调用openSession获得SqlSession对象,然后再用这个SqlSession对象调用getMapper方法从configuration对象中的mapperRegistry获得已经被MapperProxyFactory工厂反向代理过的mapper接口对象。接着调用mapper接口的方法即可。

那么Spring集成的MapperFactoryBean使用方式和原生的有何不同?接下来一起看看。

applicationContext.xml配置:

<bean id="demoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.iboxpay.mapper.DemoMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

可以看到该配置将刚刚构造完成的sqlSessionFactory赋值给MapperFactoryBean,并且指定了对应的mapper。接下来看到其关键源码部分:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport 
        implements FactoryBean<T> {
  // 被封装的接口类型,FactoryBean调用getObject将会返回这个类型Bean
  private Class<T> mapperInterface;
  private boolean addToConfig = true;
  @Override
  protected void checkDaoConfig() {
    // 可以看到这个方法是重写的,因此父类肯定有操作,父类的流程我们等下再去分析
    super.checkDaoConfig();
    // 封装的接口类型不能为空
    notNull(this.mapperInterface, "...");
    // 在父类中已经设置过了sqlSession,因此这里的sqlSession是必有值的
    Configuration configuration = getSqlSession().getConfiguration();
    // 默认会把mapperInterface添加到configuration对象中的mapperRegistry对象中
    if (this.addToConfig && 
            !configuration.hasMapper(this.mapperInterface)) {
      try {
        // 同时添加到configuration中,保持一致
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }
  // 其它方法略过,只看这个方法
  @Override
  public T getObject() throws Exception {
    // 实际还是会调用configuration的getMapper方法,从mapperRegistry中获取
    // mapper接口的代理对象,类型是MapperProxy
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

看到这个类肯定一脸懵逼,其父类中的操作将会解除我们的疑惑。

2.1 DaoSupport类

这是Spring为持久化框架设置的一个抽象类,这个类实现了InitializingBean接口,其关键源码如下:

public abstract class DaoSupport implements InitializingBean {
    @Override
    public final void afterPropertiesSet() throws IllegalArgumentException,
            BeanInitializationException {
       // 检查Dao类的配置
       checkDaoConfig();
       // 交给子类来实现初始化dao的操作
       try {
           // 方法交给子类去实现
           initDao();
       }
       catch (Exception ex) {
          throw new BeanInitializationException();
       }
    }
    protected abstract void checkDaoConfig() 
            throws IllegalArgumentException;
    protected void initDao() throws Exception {
    }
}

2.2 SqlSessionDaoSupport

这个类集成了Spring为持久化框架实现的抽象类DaoSupport,让我们看看该类的具体实现源码:

public abstract class SqlSessionDaoSupport extends DaoSupport {
  private SqlSessionTemplate sqlSessionTemplate;
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    // 这个方法是搭配配置sqlSessionFactory使用的,如果配置了sqlSessionFactory
    // 那么这个方法将一定会被调用,因为Spring工厂初始化Bean的时候会递归其setter
    // 方法,如果工厂里有这个类型的Bean,将会被赋值进来;
    // 如果本类的sqlSessionTemplate为空或者sqlSessionTemplate中的
    // sqlSessionFactory对象和传入的不一致,则调用createSqlSessionTemplate方法
    // 创建sqlSessionTemplate对象
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this
            .sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = 
              createSqlSessionTemplate(sqlSessionFactory);
    }
  }
  protected SqlSessionTemplate createSqlSessionTemplate(
          SqlSessionFactory sqlSessionFactory) {
    // sqlSessionFactory是Spring工厂传入的Bean
    return new SqlSessionTemplate(sqlSessionFactory);
  }
  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate){
    // 如果Spring工厂中创建了sqlSessionTemplate对象,则会调用进来并赋值
    this.sqlSessionTemplate = sqlSessionTemplate;
  }
  @Override
  protected void checkDaoConfig() {
    // 实例化之后,将会判断sqlSessionTemplate不为空
    notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or"+
            " 'sqlSessionTemplate' are required");
  }
}

详细说明看代码中的注释。截止到这,其实Spring工厂已经获得了目标mapper接口的代理对象工厂,并且也已经交给了Spring工厂管理,等使用@Autowired等注解时便会自动调用MapperFactory-Bean工厂Bean的getObject方法,像原生Mybatis使用方法一样,调用getMapper获得mapper接口的代理对象MapperProxy。

3.SqlSessionTemplate

前面我们已经看到了,在SqlSessionDaoSupport类中,Mybatis根据Spring工厂初始化Bean的流程中,使用setter机制设置了sqlSessionFactory对象,并初始化了一个SqlSessionTemplate对象,并且如果Spring工厂中有这个对象,又再会调用settter方法设置SqlSessionTemplate。

因此这个类在mybatis集成到spring中是绝对需要分析一下的,接下来看看SqlSessionTemplate的关键源码部分是如何实现的:

public class SqlSessionTemplate implements SqlSession, DisposableBean {
  // 配置的sqlSessionFactory对象
  private final SqlSessionFactory sqlSessionFactory;
  // 配置的执行类型,有Simple,有Batch等
  private final ExecutorType executorType;
  // 内部实际代理的SqlSession对象,调用selectOne和selectList这些方法
  // 实际将会调用到这个对象来,会使用内部类SqlSessionInterceptor增加方法
  private final SqlSession sqlSessionProxy;
  
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, 
          ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // 根据SqlSessionFactory和SqlSession接口使用SqlSessionInterceptor
    // 代理对象增加
    this.sqlSessionProxy = (SqlSession) 
            newProxyInstance(SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class }, new SqlSessionInterceptor());
  }
  // 下面是一系列SqlSession的接口方法,都是调用sqlSessionProxy的相应方法,略
  ...
  // 加强sqlSessionProxy对象的内部代理类,使用JDK动态代理增强,因为SqlSession
  // 本身就是一个接口
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
      // 根据sqlSessionFactory和executorType对象获取session,方法内部实际上
      // 也调用了sqlSessionFactory的openSession方法,只是前面会判断当前
      // 是否含有sqlSession对象,因此这里的SqlSession对象还是DefaultSqlSession
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this
              .sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this
                  .exceptionTranslator);
      try {
        // 调用原代理方法,即外部的mapper接口方法实际上调用的还是sqlSession
        // 对象的方法
        Object result = method.invoke(sqlSession, args);
        // 如果事务没有被Spring事务管理器管理则调用sqlSession.commit手动提交
        // 否则交给Spring事务管理去操作事务流程
        if (!isSqlSessionTransactional(sqlSession, 
                SqlSessionTemplate.this.sqlSessionFactory)) {
          // 手动提交事务
          sqlSession.commit(true);
        }
        // 返回结果
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && 
                unwrapped instanceof PersistenceException) {
          // 出错则关闭会话
          closeSqlSession(sqlSession, 
                  SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate
              .this.exceptionTranslator.translateExceptionIfPossible(
                      (PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        // 关闭会话
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate
                  .this.sqlSessionFactory);
        }
      }
    }
  }
}

在spring框架使用具体的mapper类时,将会通过MapperFactoryBean的getObject来获得具体的mapper对象,并注入使用的类中,getSqlSession().getMapper(this.mapperInterface)是该方法中的具体代码,到此,基本上就完成了mybatis原生过程的getMapper,接下来只需要调用mapper中的方法即可。

也可以看到SqlSessionTemplate类中实现了SqlSession接口,而具体的实现方法则是直接调用sqlSessionProxy的方法,并且为SqlSessionTemplate的每次方法调用也添加了一层代理。

到此,使用MapperFactoryBean方式和spring集成原理便结束了。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值