事物的處理————原始碼分析
事物的處理是在工程的業務邏輯層,也就是通常所說的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沒有實現任何的通知,我就很迷惑他是如何作為切面的,我認為的切面是實現了通知(前置,後置,異常,環繞),而且也沒有用到任何的註解。。。
希望得到高人的指點。。。