Spring之執行事務流程
阿新 • • 發佈:2018-12-12
上一篇文章我們已經對配置檔案進行了解析,對bean的例項化過程也和之前的差不多,就不再分析了,我們這次主要的任務就是看一下事務是怎麼在Spring中執行的:
userService.save();
userService.update();
這兩行程式碼是展開本文的入口:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
...
}
由於我們在配置事務AOP的時候,讓他掃描了service所在的包,所以,在建立UserService 的時候新增到IOC容器中的是它的 代理類,所以在執行它的save方法的時候直接進入了invoke方法:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
獲得針對這個方法的所有攔截器
[[email protected]74bada02,
org[email protected]525575]
發現有兩個,一個是自動新增的攔截器,另外一個就是有關事務的攔截器了
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
將這個代理類的一些資訊封裝成MethodInvocation
retVal = invocation.proceed();
開始從攔截器鏈的頭部開始執行,我們直接跳到事務的那個攔截器的處理邏輯上:
public Object invoke(final MethodInvocation invocation) throws Throwable { // 獲得目標類的class物件 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
這個方法首先獲得了目標類的class物件,然後開始執行invokeWithinTransaction,由於這個方法比較長,像以前一樣,我們一部分一部分的執行:
//獲得事務的封裝物件
TransactionAttributeSource tas = getTransactionAttributeSource();
//獲得當前方法的事務屬性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//獲得事務管理類,這個主要是根據我們選擇的資料來源有關的
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//獲得方法完整的限定名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
這段程式碼針對當前方法獲得了事務的屬性,方法的限定名,和事務管理類
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
根據給定的TransactionAttribute建立一個事務,下面是這個方法完整的程式碼:
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果沒有指定名稱,則應用方法標識作為事務名稱。
if (txAttr != null && txAttr.getName() == null) {
//封裝事務的屬性
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//獲得當前事務的狀態
status = tm.getTransaction(txAttr);
}
...
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
我們繼續看一下TransactionStatus 物件是怎麼獲取到的:
Object transaction = doGetTransaction();
首先執行了這個方法:
protected Object doGetTransaction() {
//獲取資料來源的事務物件
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
//設定回滾點
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//獲得封裝連線的物件
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//填充屬性
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
這個方法建立了一個數據源的事務物件
//是否需要同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//封裝DefaultTransactionStatus
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//開始執行事務
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
我們終於看到了要開始執行事務了,走進doBegin看一下:
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//獲得資料來源的連線
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
//將連線設定到事務物件中
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//設定同步事務
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
//獲得連線
con = txObject.getConnectionHolder().getConnection();
//獲得隔離級別
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
//禁止自動提交,開啟事務
con.setAutoCommit(false);
}
//事務開始後立即準備事務
prepareTransactionalConnection(con, definition);
//設定事務已經開始
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 將連線繫結到執行緒。
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
在這個方法中設定了事務的屬性並開啟了事務
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
//例項化一個新的當前事務的資訊
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
//添加當前事務的狀態
txInfo.newTransactionStatus(status);
}
// 綁定當前執行緒
txInfo.bindToThread();
return txInfo;
}
建立了當前事務的資訊,最終返回到了這個方法中invokeWithinTransaction,接下來的程式都是執行在事務開啟環境當中的:
retVal = invocation.proceedWithInvocation();
進入這個方法後由於攔截器已經執行完了,所以下面執行的是目標方法
invokeJoinpoint();
新增事務後,我們發現其實和以前講AOP的流程沒有什麼差別,無非是在事務的攔截器中開啟了事務,目標方法結束後提交了事務。