1. 程式人生 > >事物的處理————原始碼分析

事物的處理————原始碼分析

事物的處理是在工程的業務邏輯層,也就是通常所說的service層,但是Connection和Transaction我們通常是在dao層,也就是持久化層來獲取的,那麼連線的釋放和事物的提交回滾是如何實現的呢?

我們從連線的獲取說起:
connection我們通常是從連線池中獲取的,我們可能通過一句程式碼,比如:

conn = ConnectionPool.getConnection();

下面我們來看看他的底層程式碼

ConnectionPool實際是我們自己封裝的一個工具類,他利用“門面模式”,將一些複雜的程式碼封裝起來,只給呼叫者提供一個言簡意賅的介面,其實內部呼叫的是spring給我們提供的一個介面DataSourceUtils.doGetConnection(dataSource),同時設定conn的提交方式

public static synchronized Connection getConnection(String dataSourceName, boolean autoCommit) {

        Connection conn = null;

        DataSource dataSource = dataSourceHelper.getDataSource(dataSourceName);

        try {
            //實際獲取方式
            conn = DataSourceUtils.doGetConnection(dataSource);

            if
(conn != null && !conn.isClosed()) { //設定是否自動提交 conn.setAutoCommit(autoCommit == true ? true : false); if (log.isInfoEnabled()) { log.info("a connection was got[active: " + getConnCount() + "]"); } } return
conn; } catch (Exception e) { throw new WAFException(e); } }

其中需要提供引數DataSource,我們可以看到他也是被封裝起來的,封裝的方式有很多,讓我們一起來看一下在我的專案中是如何封裝的:

@Component("dataSourceHelper")
public class DataSourceHelper {

    // 預設資料來源名
    private String defaultDataSourceName = "";
    //注入dataSourceMap
    @Resource(name="dataSourceMap")
    private Map<String, DataSource> dataSourceMap;
/**
     * 根據引數中的資料來源名取得資料來源物件
     * 
     * @param dataSourceName 資料來源名
     * @return 資料來源名對應的資料來源物件
     */
    DataSource getDataSource(String dataSourceName){
        return dataSourceMap.get(dataSourceName);
    }

這樣我們就可以在配置檔案中配置多個數據源,然後注入到DataSourceHelper 進行統一管理,我們可以利用不同的名稱來獲取不同的資料來源

提供配置檔案:

 <util:map id="dataSourceMap" 
              map-class="java.util.LinkedHashMap" 
              key-type="java.lang.String" 
              value-type="javax.sql.DataSource">

        <!-- 開發平臺主資料來源 -->
        <entry key="dbcpDataSource" value-ref="dbcpDataSource" />

        <!-- 關聯資料來源1 
        <entry key="dbcpDataSource_1" value-ref="dbcpDataSource_1" />
        -->

        <!-- 關聯資料來源N 
        <entry key="dbcpDataSource_N" value-ref="dbcpDataSource_N" />
        -->

    </util:map>

看別人的程式碼,提升自己的能力,上面所說的是在我的工程中,連線的獲取是如何來封裝的,下面我們來看一下在spring中connection是如何處理的

一起看一下DataSourceUtils.doGetConnection(dataSource)

  public static Connection doGetConnection(DataSource dataSource)
    throws SQLException
  {
    Assert.notNull(dataSource, "No DataSource specified");
    //從TransactionSynchronizationManager獲取ConnectionHolder,他其實是將connection進行了再一次的封裝,內部提供有getConnection和setConnection方法
    ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);

//如果conHolder 不為空,從conHolder中獲取連線 
    if ((conHolder != null) && ((conHolder.hasConnection()) || (conHolder.isSynchronizedWithTransaction())))
    {
      conHolder.requested();
      if (!conHolder.hasConnection())
      {
        logger.debug("Fetching resumed JDBC Connection from DataSource");
        conHolder.setConnection(dataSource.getConnection());
      }
      return conHolder.getConnection();
    }

//如果conHolder為空的話,建立新的conHolder,存放在TransactionSynchronizationManager中
    logger.debug("Fetching JDBC Connection from DataSource");
    Connection con = dataSource.getConnection();
    if (TransactionSynchronizationManager.isSynchronizationActive())
    {
      logger.debug("Registering transaction synchronization for JDBC Connection");


      ConnectionHolder holderToUse = conHolder;
      if (holderToUse == null) {
        holderToUse = new ConnectionHolder(con);
      } else {
        holderToUse.setConnection(con);
      }
      holderToUse.requested();
      TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));

      holderToUse.setSynchronizedWithTransaction(true);
      if (holderToUse != conHolder) {
      //將ConnectionHolder 放入TransactionSynchronizationManager
        TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
      }
    }
    return con;
  }

TransactionSynchronizationManager.getResource(dataSource);

public static Object getResource(Object key)
  {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Object value = doGetResource(actualKey);
    if ((value != null) && (logger.isTraceEnabled())) {
      logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
    }
    return value;
  }

  private static Object doGetResource(Object actualKey)
  {
  //注意這裡resources.get()
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
      return null;
    }
    Object value = map.get(actualKey);
    if (((value instanceof ResourceHolder)) && (((ResourceHolder)value).isVoid()))
    {
      map.remove(actualKey);
      if (map.isEmpty()) {
        resources.remove();
      }
      value = null;
    }
    return value;
  }

來一起看看resources是個什麼東西:

public abstract class TransactionSynchronizationManager
{
  private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
  private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
  private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
  private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
  private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
  private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
  private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");

哦….果然,他是把生成的ConnectionHolder當做value,以DataSource為key儲存在map中,然後將map放在了ThreadLocal當中,他這裡NamedThreadLocal其實就是繼承ThreadLocal,然後添加了一條name屬性

等等,為什麼要說果然呢?其實我們想一下應該能夠猜到,回到文章開頭提出的問題,dao層的connection如何在service層進行處理,那肯定是得在service層能夠獲取到connection,所以應該會用到快取,但是一般的快取在放入connection作為value之後,我們並不知道用什麼來作為他的key,因為根本沒法取值,所以這裡我們應該能夠想到ThreadLocal,用當前的執行緒作為key,這樣我們就可以很容易的利用ThreadLocal中的get()方法獲取到connection,從而進行事物的處理

利用aop思想,我認為用來管理事物的DataSourceTransactionManager一定是實現了環繞通知,通過切入點方法是否異常來執行事物的回滾和提交

但是。。。

DataSourceTransactionManager沒有實現任何的通知,我就很迷惑他是如何作為切面的,我認為的切面是實現了通知(前置,後置,異常,環繞),而且也沒有用到任何的註解。。。

希望得到高人的指點。。。