写点什么

面试被问 Mybatis 底层实现:你连这个知识点都说不明白?

用户头像
小Q
关注
发布于: 2020 年 11 月 29 日

面试真的是越来越难了,一直关注我的朋友应该清楚,小编有一个习惯,喜欢每年都去面试一下,试试水也看看自己的缺陷,这不,前几天有一个机会,让小编成功的被刺激了一耙(虽然知道这样不是很好,但是还是想操作一下,给各位 hr 道歉)


事情是这样的,小编的简历一直挂在网上,然后有人给我打电话,说有没有时间去面试一下,刚过完双十一,闲着也是闲着,我就应下来去了,然后前面谈的都不错,直到要结束最后一下面试,马上就是 hr 面了,面试官脑回路不知道怎么回事,就问了我一个意想不到的问题:看你简历写的精通 mybatis 源码,那你能不能给我讲一下 sqlsession 啊,WTF?我就反问一句,这有点多啊,那我主要说那一块呢?


1、SqlSession 操作模板实现类==SqlSessionTemplate

2、用于保存当前 SqlSession 对象==SqlSessionInterceptor

3、SqlSession 的事务同步器==SqlSessionSynchronization


幸好我之前看过这一块,然后磕磕绊绊的讲完了,面试官眼神复杂的看了我一下,说可以了,先这样吧。。。。


公众号:Java 架构师联盟,每日更更新技术好文


偶,你 NNGT,我这美好的一天,就这么没了,面试完了也没啥心思去吃好吃的,回到家就开始冲浪,将这几个知识点进行整理



其实官网上,sqlsession 相关得知识还是很多的,但是时间问题,就针对源码整理被问到的这几个知识点,大家有什么其他想看的可以评论区告诉我,我会抽时间进行整理,好了,话不多说,看重点


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. 调用 SqlSessionUtils 的 getSqlSession 方法,获取一个 SqlSession 对象

  2. 执行 SqlSession 的方法

  3. 当前 SqlSession 不处于 Spring 托管的事务中,则强制提交

  4. 调用 SqlSessionUtils 的 closeSqlSession 方法,“关闭”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. 设置 holder 的 synchronizedWithTransaction 属性为 ture,和事务绑定了

  6. 增加 holder 的 referenceCount 引用数量


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();
复制代码


}
复制代码

总结


我这次面试的公司不大,只能算是一家中型公司,也没有什么说面试前的准备,都是平时学习的记忆,毕竟目的不一样,但是,同样也反映出一个真实情况:在日常工作中要多注意一些细节,当出现问题的时候在解决完,有时间的情况下多深入看一下源码的东西,其实源码真的不难,嘿嘿嘿,好了,整理完了就觉得高兴多了,看时间还早,夜生活该开始了



祝周末愉快


发布于: 2020 年 11 月 29 日阅读数: 62
用户头像

小Q

关注

还未添加个人签名 2020.06.30 加入

小Q 公众号:Java架构师联盟 作者多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!

评论

发布
暂无评论
面试被问Mybatis底层实现:你连这个知识点都说不明白?