aop即Aspect-Oriented Programming ,面向切面編程。

  • Aspect:切面。在代碼的執行過程中,總是有一些邏輯在多個模塊中是一樣的,這個時候,這些多個處理邏輯一樣的地方就可以放在一個地方處理。這種處理就感覺像是在代碼的各個模塊文件中,橫向切開了一刀,插入額一段新的邏輯,這些新邏輯的代碼文件像是橫叉在所有代碼的一個切面,經過這個平面處理之後再回到原有的執行邏輯
  • Join Point:公共程序執行的位置,對於spring來說,表示方法的執行(要支持欄位的更新可以選擇AspectJ)
  • Point cut:適配所有Join Point的表達式,用來篩選是和執行公共代碼的方法
  • Advice:切面中實際執行的代碼,它可以在方法執行前、執行後、扔出異常等等的位置
  • Target Object:切面執行完後,原始程序需要執行的內容,對於切面來說,這個就是需要它代理要執行的對象
  • Advisor:代碼實現,負責組織好 Advice/Point cut/要代理的對象 的關係

Aop代碼中運用

可以使用xml或者註解的方式在項目中使用aop,以註解為例,一般使用可以引用 AspectJ,自己創建一個類,在類上標註好註解 @Aspect

@Aspect
public class LogAspect {}

在xml中開啟掃描即可找到這個註解

<aop:aspectj-autoproxy />

在代碼中建立好對應的Point Cut

@Pointcut("execution(* paxi.maokitty.verify.spring.aop.service.ExecuteService.*(..))")
public void allClassPointCut(){}

這裡PointCut表達式指定類paxi.maokitty.verify.spring.aop.service.ExecuteService所有方法都是目標對象

建立自己需要執行的方法(advice)

@Before("allClassPointCut()")
public void beforeAspectExecuteService(JoinPoint joinPoint){
LOG.info("beforeAspectExecuteService execute method:{}",new Object[]{joinPoint.getStaticPart().toShortString()});
}

即可達到對應的目標,而且這種方式做到了對原有代碼的無入侵,體驗很好。完整的可運行實例請戳這裡

spring中的事務對aop的使用

事務

此處不討論分散式事務

事務是資料庫執行過程中的一個邏輯單位,由一個有限的資料庫操作序列構成。當事務被提交給了資料庫,資料庫需要確保該事務中的所有操作都成功完成並且結果被永遠保存在資料庫中。如果事務中有的操作沒有成功的完成,則事務中的所有操作都需要回滾,回到事務執行前的狀態,同時,該事務對資料庫的其他事務執行沒有影響。資料庫事務一般擁有以下四個特性

  • 原子性:事務中所有的操作要麼都成功要麼都失敗
  • 一致性:確保資料庫從一個一致狀態轉變成另一個一致的狀態
  • 隔離性:多個事務並發執行不會互相影響
  • 持久性:已被提交的事務對資料庫的修改應該永遠的保存在資料庫中

java對事務的代碼實現

java中操作資料庫操作的關鍵類是 Connection ,它代表了對資料庫的一個連接,通過對應的方法

  • connection.commit():執行事務的提交語義
  • con.rollback();:執行事務的回滾語義 可以控制事務操作

spring中運用事務

spring中最簡單的實現只需要直接在要使用事務的類上添加註解 @Transactional,並在xml中添加註解的掃描<tx:annotation-driven transaction-manager="txManagerTest"/>基本就可以利用spring的事務了

spring對事務的實現則是通過aop來實現的。spring在掃描tx標籤的時候,碰到transactional標註的類或者方法,會創建對應的AOP代理,在調用的時候則是AOP代理去執行,先按照AOP的方式執行相應的邏輯,再執行用戶定義的方法,如果有問題則執行對應的事務

@Trace(
index = 13,
originClassName = "org.springframework.transaction.interceptor.TransactionAspectSupport",
function = "protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable"
)
public void invokeWithinTransaction(){
//...
Code.SLICE.source("final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);")
.interpretation("查到對應方法的事務配置");
Code.SLICE.source("final PlatformTransactionManager tm = determineTransactionManager(txAttr);")
.interpretation("拿到transactionManager,比如用戶在xml中配置的 org.springframework.jdbc.datasource.DataSourceTransactionManager");
Code.SLICE.source("final String joinpointIdentification = methodIdentification(method, targetClass);")
.interpretation("獲取transaction標註的方法");
//...
Code.SLICE.source("TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
" +
" Object retVal = null;
" +
" try {
" +
" retVal = invocation.proceedWithInvocation();
" +
" }
" +
" catch (Throwable ex) {
" +
" // target invocation exception
" +
" completeTransactionAfterThrowing(txInfo, ex);
" +
" throw ex;
" +
" }
" +
" finally {
" +
" cleanupTransactionInfo(txInfo);
" +
" }
" +
" commitTransactionAfterReturning(txInfo);
" +
" return retVal;")
.interpretation("這裡就是標準的事務處理流程 1:獲取事務;2:執行用戶自己的方法;3:如果執行過程中拋出了異常執行異常拋出後的事務處理邏輯 4:清除事務信息 5:提交事務");
//...
}

spring 事務具體執行邏輯

spring自定義了事務的傳播邏輯

  • PROPAGATION_REQUIRED :如果沒有事務就新建一個,有的話就在那個事務裡面執行。默認配置
  • PROPAGATION_SUPPORTS:沒有事務就什麼都不做,有事務則在當前事務中執行
  • PROPAGATION_MANDATORY:如果沒有事務就拋出異常
  • PROPAGATION_REQUIRES_NEW:創建新的事務,如果已經存在一個事務,就先把這個事務暫停,執行完新建的事務之後再恢復
  • PROPAGATION_NOT_SUPPORTED:方法不會在事務中執行,如果存在事務,會在方法執行期間被掛起
  • PROPAGATION_NEVER:如果有事務就拋出異常
  • PROPAGATION_NESTED:如果已經有一個事務,就再嵌套一個執行,被嵌套的事務可以獨立於封裝事務進行提交或者回滾,如果不存在事務,則新建事務

這裡就注意到 所謂 物理事務 和 邏輯事務的區別

  • 物理事務就是底層資料庫提供的事務支持
  • 邏輯事務則是spring自己管理的事務,它與物理事務最大的區別就在於事務的傳播行為,即多個事務在方法間調用時,事務是如何傳播的

spring對事務的隔離機制

  • TRANSACTION_READ_UNCOMMITTED :在一個事務中一行數據的改變,再實際提交之前,會被另一個事務讀到,也就是說如果改變數據的事務發生回滾,那麼其它線程讀到的數據就是無效的
  • TRANSACTION_READ_COMMITTED:事務一行數據的改變,只有在數據提交之後才能讀到
  • TRANSACTION_REPEATABLE_READ: 事務一行數據的改變,只有在數據提交之後才能讀到。兩個事務,一個事務讀到一行數據,另一個事務立馬進行了修改,如果第一個事務對數據再次進行讀取,此時它讀到的數據還和之前一樣
  • TRANSACTION_SERIALIZABLE:一個事務讀取到滿足where條件的數據之後,另一個事務同時插入了一行滿足這個條件的數據,第一個事務再次讀取並不會讀到這個新的數據

事務的隔離機制與傳播機制源碼註解解釋各自含義,實際就是Connection的定義

對不用的隔離機制,也就產生了 臟讀、不可重複讀、幻讀的場景

Y表示會存在,N表示不存在

實質上就是在不同隔離機制下,多個事務讀取數據的影響

spring的具體源碼實現

spring自定義的傳播機制,實際上就是代碼的處理邏輯,在不同的場景下做出的限制

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation never");
}

它底層去提交事務或是回滾事務,本質上還是java的Connection來最終執行操作,另外對於對於一次訪問的多個資料庫的事務操作,spring自己將連接與線程建立了關聯關係,即每個線程都持有的是同一個連接,來保證期望相同的資料庫操作在同一個事務裡面。


推薦閱讀:
相关文章