ibatis源码分析(番外篇)

主要以线程的数量为主的分析

问题1:线程安全

    在整个app多线程中,可以共用一个SqlMapClient来执行操作。原理是利用了ThreadLocal,ThreadLocal实际上是不同的Thread以ThreadLocal自身对象为key的一份ThreadLocalMap拷贝,每个thread创建了各自的Map,这个Map的key就是ThreadLocal的hashcode,所以,同一个ThreadLocal存储的不同value会存储在Map中table数组的同一个index上,并组成了链表。不同的ThreadLocal则存储在Thread中不同的index处,这个Map也不可能大小总为1;

在sqlMapClientImpl里面获取sqlMapSessionImpl对象

  protected SqlMapSessionImpl getLocalSqlMapSession() {
    SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
    if (sqlMapSession == null || sqlMapSession.isClosed()) {
      sqlMapSession = new SqlMapSessionImpl(this);
      localSqlMapSession.set(sqlMapSession);
    }
    return sqlMapSession;
  }

这是对一个线程的,new SqlMapSessionImpl()对象的构造器在SqlMapSessionImpl类里面:

  /**
   * Constructor
   *
   * @param client - the client that will use the session
   */
  public SqlMapSessionImpl(ExtendedSqlMapClient client) {
    this.delegate = client.getDelegate();
    this.session = this.delegate.popSession();
    this.session.setSqlMapClient(client);
    this.session.setSqlMapExecutor(client);
    this.session.setSqlMapTxMgr(client);
    this.closed = false;
  }
public class SqlMapSessionImpl implements SqlMapSession {


  protected SqlMapExecutorDelegate delegate;
  protected SessionScope session;
  protected boolean closed;  
/**
   * Constructor
   *
   * @param client - the client that will use the session
   */
  public SqlMapSessionImpl(ExtendedSqlMapClient client) {
    this.delegate = client.getDelegate();
    this.session = this.delegate.popSession();
    this.session.setSqlMapClient(client);
    this.session.setSqlMapExecutor(client);
    this.session.setSqlMapTxMgr(client);
    this.closed = false;
  }

这里看到,真正做事的是SqlMapExecutorDelegate这个类,并且还注意到,这里其实把SqlMapExecutorDelegate这个实现类传递给了sqlMapSession,又封装了一层。

问题2:Session上下文(事务控制)

每个SessionScope管理自己的事务,在这个事务中执行过的所有sql语句。这里用的是代理模式,sqlMapSessionImpl和SqlMapExecutorDelegate是集成相同的接口sqlMapExecutor和SqlMapTransactionManager.

从上面的代码可以看出,sqlMapSessionImpl构造了一个sessionScope对象,sessionScope是装饰模式,是一个threadLocal主要的工作类。它被SqlMapExecutorDelegate中的一个pool维护着,

,到此,可以看到很多操作都包装在了SqlMapExecutorDelegate类里。

  SessionScope对象池用Collections.synchronizedList(new ArrayList(size));线程安全的List来存储,因为所有SessionScope都公用同一个SqlMapExecutorDelegate。

SqlMapExecutorDelegate类: 
 protected SessionScope popSession() {
    return (SessionScope) sessionPool.pop();
  }
public class SqlMapExecutorDelegate {

  private static final Probe PROBE = ProbeFactory.getProbe();

  /**
   * The default maximum number of requests
   */
  public static final int DEFAULT_MAX_REQUESTS = 512;
  /**
   * The default maximum number of sessions
   */
  public static final int DEFAULT_MAX_SESSIONS = 128;
  /**
   * The default maximum number of transactions
   */
  public static final int DEFAULT_MAX_TRANSACTIONS = 32;

  private boolean lazyLoadingEnabled;
  private boolean cacheModelsEnabled;
  private boolean enhancementEnabled;

  private int maxRequests = DEFAULT_MAX_REQUESTS;
  private int maxSessions = DEFAULT_MAX_SESSIONS;
  private int maxTransactions = DEFAULT_MAX_TRANSACTIONS;

  private TransactionManager txManager;

  private HashMap mappedStatements;
  private HashMap cacheModels;
  private HashMap resultMaps;
  private HashMap parameterMaps;

  private ThrottledPool requestPool;
  private ThrottledPool sessionPool;

  protected SqlExecutor sqlExecutor;
  private TypeHandlerFactory typeHandlerFactory;
  private DataExchangeFactory dataExchangeFactory;
  
  private ResultObjectFactory resultObjectFactory;
  private boolean statementCacheEnabled;

  /**
   * Default constructor
   */
  public SqlMapExecutorDelegate() {
    mappedStatements = new HashMap();
    cacheModels = new HashMap();
    resultMaps = new HashMap();
    parameterMaps = new HashMap();

    <span style="color:#ff0000;">requestPool = new ThrottledPool(RequestScope.class, DEFAULT_MAX_REQUESTS);//默认请求的池的大小是512</span>
   <span style="color:#ff0000;"> sessionPool = new ThrottledPool(SessionScope.class, DEFAULT_MAX_SESSIONS);//默认session池的大小是128</span>

    sqlExecutor = new SqlExecutor();
    typeHandlerFactory = new TypeHandlerFactory();
    dataExchangeFactory = new DataExchangeFactory(typeHandlerFactory);
  }
public class ThrottledPool {

  private Throttle throttle;

  private Class type;
  private List pool;

  /**
   * Create a ThrottledPool for a Class
   * @param type - the type of objects being managed
   * @param size - the size of the pool
   */
  public ThrottledPool(Class type, int size) {
    try {
      this.throttle = new Throttle(size);
      this.type = type;
      this.pool = Collections.synchronizedList(new ArrayList(size));<span style="color:#ff0000;">//size是以tyep类型的对象的默认大小为准,例如requestPool的默认大小是512</span>
      for (int i=0; i < size; i++) {
        this.pool.add(type.newInstance());
      }
    } catch (Exception e) {
      throw new RuntimeException("Error instantiating class.  Cause: " + e, e);
    }
  }

  /**
   * Pop an object from the pool
   * @return - the Object
   */
  public Object pop() {
    throttle.increment();
    return pool.remove(0);<span style="color:#ff0000;">//池中的对象都是按照队列的形式保存,也就是以队列的形式处理,即先进先出</span>
  }

public class Throttle {

  private final Object LOCK = new Object();

  private int count;
  private int limit;
  private long maxWait;

  /**
   * Create a throttle object with just a limit
   * @param limit - the number of references to allow
   */
  public Throttle(int limit) {
    this.limit = limit;
    this.maxWait = 0;
  }

  /**
   * Create a throttle object with a limit and a wait time
   * @param limit - the number of references to allow
   * @param maxWait - the maximum wait time allowed for a reference
   */
  public Throttle(int limit, long maxWait) {
    this.limit = limit;
    this.maxWait = maxWait;
  }

  /**
   * Add a reference; if a reference is not available, an exception is thrown
   */
  public void increment() {
    <span style="color:#ff0000;">synchronized (LOCK) {</span>
      long totalWaitTime = 0;
      while (count >= limit) {
        if (maxWait > 0) {
          long waitTime = System.currentTimeMillis();
          try {
            LOCK.wait(maxWait - totalWaitTime);
          } catch (InterruptedException e) {
            //ignore
          }
          totalWaitTime += System.currentTimeMillis() - waitTime;
          if (totalWaitTime > maxWait) {
            throw new RuntimeException("Throttle waited too long (" + totalWaitTime + " milliseconds) for lock.");
          }
        } else {
          try {
            LOCK.wait();
          } catch (InterruptedException e) {
            //ignore
          }
        }
      }
      count++;
    }
  }

至此,是获取localSqlMapSession即SqlMapSessionImpl对象的流程。

回到最初的代码:

 SqlMapClientImpl类:
public List queryForList(String id, Object paramObject) throws SQLException {
    return getLocalSqlMapSession().queryForList(id, paramObject);
  }

 SqlMapSessionImpl类:
 public List queryForList(String id, Object paramObject) throws SQLException {
    return delegate.queryForList(session, id, paramObject);
  }

SqlMapExecutorDelegate类:
  
/**
   * Execute a query for a list
   *
   * @param session     - the session scope
   * @param id          - the statement ID
   * @param paramObject - the parameter object
   * @return - the data list
   * @throws SQLException - if the query fails
   */
  public List queryForList(SessionScope session, String id, Object paramObject) throws SQLException {
    return queryForList(session, id, paramObject, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS);
  }

/**
<pre name="code" class="html" style="font-size: 14px; line-height: 25.2000007629395px;">SqlMapExecutorDelegate类:
* Execute a query for a list * * @param session - the session scope * @param id - the statement ID * @param paramObject - the parameter object * @param skip - the number of rows to skip * @param max - the maximum number of rows to return * @return - the data list * @throws SQLException - if the query fails */ public List queryForList(SessionScope session, String id, Object paramObject, int skip, int max) throws SQLException { List list = null; MappedStatement ms = getMappedStatement(id); Transaction trans = getTransaction(session); boolean autoStart = trans == null;//这边必须强调一下,之前很纠结,误读为autostart这个参数是false,其实这边是判断trans是否为Null,是null,z则为true,否则为false.

 
这里对于获取transaction很重要。因为是true的时候,就要去新生成这个session的transaction.

    try {
      trans = autoStartTransaction(session, autoStart, trans);

      RequestScope request = popRequest(session, ms);
      try {
        list = ms.executeQueryForList(request, trans, paramObject, skip, max);
      } finally {
        pushRequest(request);
      }

      autoCommitTransaction(session, autoStart);
    } finally {
      autoEndTransaction(session, autoStart);
    }

    return list;
  }
  /**
   * Get a transaction for the session
   *
   * @param session - the session
   * @return - the transaction
   */
  public Transaction getTransaction(SessionScope session) {
    return session.getTransaction();
  }

   id是XML中定义SQL语句的名称,param是参数。用getMappedStatement方法,根据id来获取需要执行的MappedStatement,XML文件中所有定义的Statement都存储在一个叫mappedStatements的HashMap中,并以XML定义的名称(即Id)为key,MappedStatement对象为value,这样根据名称就取到了对应的MappedStatement。

    然后根据session来获取Transaction,如下:

 

public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }

     很显然,在session初始化的时候就已经定义好了一个Transaction,但这个Transaction不一定有值。


public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }

 

SqlMapExecutorDelegate类:

protected Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transaction trans) throws SQLException {
    Transaction transaction = trans;
    if (autoStart) {
      session.getSqlMapTxMgr().startTransaction();//<span style="font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2000007629395px;">getSqlMapTxMgr()实际上指向了SqlMapSessionImpl</span>
      transaction = getTransaction(session);
    }
    return transaction;
  }
 SqlMapClientImpl类:
public void startTransaction() throws SQLException {
    getLocalSqlMapSession().startTransaction();
  }
SqlMapSesssionOmpl类:
  public void startTransaction() throws SQLException {
    delegate.startTransaction(session);
  }
 <pre name="code" class="html" style="font-size: 14px; line-height: 25.2000007629395px;">SqlMapExecutorDelegate类:

 
 /**
   * Start a transaction on the session with the specified isolation level.
   *
   * @param session - the session
   * @throws SQLException - if the transaction could not be started
   */
  public void startTransaction(SessionScope session, int transactionIsolation) throws SQLException {
    try {
      txManager.begin(session, transactionIsolation);
    } catch (TransactionException e) {
      throw new NestedSQLException("Could not start transaction.  Cause: " + e, e);
    }
  }
TransactionManager类:
 public void begin(SessionScope session, int transactionIsolation) throws SQLException, TransactionException {
    Transaction trans = session.getTransaction();
    TransactionState state = session.getTransactionState();
    if (state == TransactionState.STATE_STARTED) {
      throw new TransactionException("TransactionManager could not start a new transaction.  " +
          "A transaction is already started.");
    } else if (state == TransactionState.STATE_USER_PROVIDED) {
      throw new TransactionException("TransactionManager could not start a new transaction.  " +
          "A user provided connection is currently being used by this session.  " +
          "The calling .setUserConnection (null) will clear the user provided transaction.");
    }


    txThrottle.increment();


    try {
      trans = transactionConfig.newTransaction(transactionIsolation);
      session.setCommitRequired(false);
    } catch (SQLException e) {
      txThrottle.decrement();
      throw e;
    } catch (TransactionException e) {
      txThrottle.decrement();
      throw e;
    }


    session.setTransaction(trans);
    session.setTransactionState(TransactionState.STATE_STARTED);
  }
SqlMapSessionImpl管理这一个SessionScope和一个SqlMapExecutorDelegate,而getSqlMapTxMgr()实际上指向了SqlMapSessionImpl,而SqlMapSessionImpl只是一个代理类,这时SqlMapSessionImpl的startTransaction()方法实际指向了SqlMapExecutorDelegate的startTransaction(Session)方法,因为SqlMapSessionImpl把持这SessionScope和SqlMapExecutorDelegate对象,所以很轻松把当前SqlMapSessionImpl中的SessionScope为参数调用SqlMapExecutorDelegate的startTransaction(Session)了,最后SqlMapExecutorDelegate调用自己的事务管理类TransactionManager把Session参数传进去,并做事务操作,startTransaction这时新建立一个Transaction对象,并把new出来的Transaction对象赋值给Session,且事务池+1,设定状态。

总结一下:SqlMapSessionImpl作为了SessionScope和SqlMapExecutorDelegate中间沟通的桥梁。


executeUpdate被封装在GeneralStatement对象里面,并其子类有DeleteStatement,SelectStatement,InsertStatement等,所有的SQL解析,执行,返回结果集都集中在这里。

 

    最后提交事务:autoCommitTransaction(session, autoStart);如果autoStart为true,则执行session中的commitTransaction()方法,类似startTransaction()一样实际调用SqlMapExecutorDelegate的commitTransaction()方法,利用TransactionManager类的public void commit(SessionScope session)方法提交事务,如下:

 

Java代码   收藏代码
  1. public void commit(SessionScope session) throws SQLException, TransactionException {  
  2.    Transaction trans = session.getTransaction();  
  3.    TransactionState state = session.getTransactionState();  
  4.    if (state == TransactionState.STATE_USER_PROVIDED) {  
  5.       } else if (state != TransactionState.STATE_STARTED && state != TransactionState.STATE_COMMITTED ) {  
  6.      throw new TransactionException("TransactionManager could not commit.  No transaction is started.");  
  7.    }  
  8.    if (session.isCommitRequired() || forceCommit) {  
  9.      trans.commit();  
  10.      session.setCommitRequired(false);  
  11.    }  
  12.    session.setTransactionState(TransactionState.STATE_COMMITTED);  
  13.  }  

 

 

    总结一下:所有的事务控制都转接给公共SqlMapExecutorDelegate类中的TransactionManager类操作,TransactionManager自己维护着一个事务池,事务的新增和销毁都在这里集中管理。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值