Spring是如何接管MyBatis的

这里的接管从使用的角度出发,不会漫无目的的分析源码,首先我们分析抽象类SqlSessionDaoSupport,这个类支持编程人员以DAO层方式来开发数据访问层,编程人员的DAO层实现只要继承这个类就可以获取到SqlSession,进而可以进行数据操作,需要了解的是,这个类是mybatis-spring包提供的,也就是说是和Spring耦合的,当我们单独使用MyBatis时,是没有这个类的,我们来看看SqlSessionDaoSupport的源码:

public abstract class SqlSessionDaoSupport extends DaoSupport {
  private SqlSession sqlSession;
  private boolean externalSqlSession;
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

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

  /**
   * Users should use this method to get a SqlSession to call its statement methods
   * This is SqlSession is managed by spring. Users should not commit/rollback/close it
   * because it will be automatically done.
   *
   * @return Spring managed thread safe SqlSession
   */
  public SqlSession getSqlSession() {
    return this.sqlSession;
  }

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

这里首先要关注的是setSqlSessionFactory()这个方法,在mybatis-spring-1.2.0及以后的版本中,这个方法删除了注释@Autowired(required = false),之前的版本是有的,也就是说Spring在实例化你写的继承于SqlSessionDaoSupport的子类到IOC容器时,不会再自动注入SqlSessionFactory,你必须手动注入,否则这个SqlSessionDaoSupport是不能正常工作的。这里还有个SqlSessionTemplate需要我们了解下:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;
  private final ExecutorType executorType;
  private final SqlSession sqlSessionProxy;
  private final PersistenceExceptionTranslator exceptionTranslator;
  
  ......

    /**
   * Proxy needed to route MyBatis method calls to the proper SqlSession got
   * from Spring's Transaction Manager
   * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
   * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
   */
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        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
          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);
        }
      }
    }
  }

}


这里可以看到SqlSessionTemplate就是对MyBatis实现的基础上再封装,使用Java动态代理的方式,可以看到代理自动将SqlSession关闭,这就是我们编程时不用openSession(),close()的原因。

猜你喜欢

转载自blog.csdn.net/tales522/article/details/80972298