日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring事务的处理流程、传播属性、及部分释疑

發布時間:2025/3/20 javascript 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring事务的处理流程、传播属性、及部分释疑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

文章目錄

  • 一、spring事務利用AOP的攔截和處理流程
        • 必須記住的兩個要點
  • 二、事務傳播屬性
  • 三、為什么很多Exception異常必須配置在rollback-for中才有用
  • 四、事務的傳播性在同一個類中方法互調時為什么會失效?

spring或是AOP參考: spring概要

一、spring事務利用AOP的攔截和處理流程


如果知道了事務的處理流程,去理解事務傳播屬性導致的回滾就是分分鐘的事兒了。

想要知道事務的攔截處理流程,只需要分析TransactionAspectSupport類的部分源碼即可

// 事務攔截處理方法 protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final TransactionAspectSupport.InvocationCallback invocation) throws Throwable {final TransactionAttribute txAttr = this.getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = this.determineTransactionManager(txAttr);final String joinpointIdentification = this.methodIdentification(method, targetClass);// 以下代碼只是部分代碼,為了方便看只截取了能說明問題的部分// 1.獲取事務TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 2.處理真正調用的方法retVal = invocation.proceedWithInvocation();} catch (Throwable var15) {// 3.有異?;貪L:this.completeTransactionAfterThrowing(txInfo, var15);throw var15;} finally {this.cleanupTransactionInfo(txInfo);}// 4.處理方法完成,提交事務this.commitTransactionAfterReturning(txInfo);return retVal;}

從上面注釋可以看到,任何一個被事務攔截的方法,都是先在真正調用該方法之前獲取了事務,執行完該方法后再決定是事務回滾或提交。

我們還可以看到,調用處理真正要執行的方法是被try catch的,而catch塊里才會有事務回滾的代碼。所以,如果某個事務方法A內部有異常沒有拋出來(被自己的try cathch塊捕獲了),而不能被這里的try catch塊捕獲到,那么這個方法A就不會被回滾。
不會回滾的例子

@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)@Overridepublic void saveTx() {try {Log log = new Log();log.setTitle("saveTx");log.setBeginDate(new Date());logService.save(log);int i=0;int j=2/i;}catch (Exception e){e.printStackTrace();}}

將會回滾的例子

@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)@Overridepublic void saveTx() { Log log = new Log();log.setTitle("saveTx");log.setBeginDate(new Date());logService.save(log);int i=0;int j=2/i; }

在我們寫代碼的時候,有時候會考慮某個方法是否應該被回滾,那么我們就得注意方法里的異常是否該被try catch是非常重要的。

還有一點,如果一個事務方法調用了一個非事務方法,那么非事務方法就要看作是事務方法的一部分,它們共用的一個數據庫連接。

必須記住的兩個要點

  • 事務攔截處理方法中,只要執行到catch塊里的事務回滾代碼才會使事務回滾【理解什么時候回滾的關鍵】。
  • 同一個事務里,任何地方導致執行了事務攔截方法里的回滾代碼,就會導致整個事務回滾【共用一個DB Conncetion】。
  • 二、事務傳播屬性


    表格中只要說到加入父事務后,就為同一個事務,他們會共用一個Connection

    傳播屬性說明
    required如果當前存在事務則加入該事務,如果沒有則新建一個事務
    supports如果當前存在事務則加入該事務,如果沒有則以非事務的方式運行。
    mandatory強制加入當前的事務。如果當前沒有事務,就拋出異常。
    requires_new新建事務。如果當前存在事務,把當前事務掛起。
    not_supported不支持事務。如果當前存在事務,就把當前事務掛起,然后以非事務方式執行。
    never以非事務方式執行,如果當前存在事務,則拋出異常。
    nested如果當前存在事務,則嵌套執行(相當于指定了回滾點SavePoint)。如果當前沒有事務,則新建事務。
    **nested** 時,如果嵌套事務發生異?;貪L,如果父事務捕獲了異常,則父事務是不會回滾的,因為它只會回滾到指定的回滾點。

    三、為什么很多Exception異常必須配置在rollback-for中才有用


    經歷過的都知道,Exception異常如果不在rollback-for屬性當中指定,即使出現了Exception異常也不會發生事務回滾。

    這是因為spring事務處理時,只對RuntimeException和Error異常進行了處理,而Exception沒有在其中。

    我們看看TransactionAspectSupport類回滾方法里的代碼:下面代碼只看注釋就行了

    protected void completeTransactionAfterThrowing(TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {if (this.logger.isTraceEnabled()) {this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);}// 判斷是否是要回滾的異常:rollbackOn方法代碼在后面if (txInfo.transactionAttribute.rollbackOn(ex)) {try {// 回滾txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());} catch (TransactionSystemException var7) {this.logger.error("Application exception overridden by rollback exception", ex);var7.initApplicationException(ex);throw var7;} catch (RuntimeException var8) {this.logger.error("Application exception overridden by rollback exception", ex);throw var8;} catch (Error var9) {this.logger.error("Application exception overridden by rollback error", ex);throw var9;}// 不在指定異常范圍內} else {try {// 事務提交 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());} catch (TransactionSystemException var4) {this.logger.error("Application exception overridden by commit exception", ex);var4.initApplicationException(ex);throw var4;} catch (RuntimeException var5) {this.logger.error("Application exception overridden by commit exception", ex);throw var5;} catch (Error var6) {this.logger.error("Application exception overridden by commit error", ex);throw var6;}}}}

    異常判斷方法:

    // RuleBasedTransactionAttribute類的方法 public boolean rollbackOn(Throwable ex) {if (logger.isTraceEnabled()) {logger.trace("Applying rules to determine whether transaction should rollback on " + ex);}RollbackRuleAttribute winner = null;int deepest = 2147483647;if (this.rollbackRules != null) {Iterator var4 = this.rollbackRules.iterator();// 遍歷rollback規則while(var4.hasNext()) {RollbackRuleAttribute rule = (RollbackRuleAttribute)var4.next();// 查找當前異常是否在規則里面int depth = rule.getDepth(ex);if (depth >= 0 && depth < deepest) {deepest = depth;winner = rule;}}}if (logger.isTraceEnabled()) {logger.trace("Winning rollback rule is: " + winner);}// 該異常沒在rollback規則里,則再去判斷是否是RuntimeException或Errorif (winner == null) {logger.trace("No relevant rollback rule found: applying default rules");// 這里調用的方法參考return super.rollbackOn(ex);} else {// rollback規則里有此異常就返true,即要回滾return !(winner instanceof NoRollbackRuleAttribute);}}

    代碼片段3:

    // 如果是RuntimeException或Error異常的子類就返回truepublic boolean rollbackOn(Throwable ex) {// 只指定了這兩個異常,沒有Exception異常return ex instanceof RuntimeException || ex instanceof Error;}

    rollback-for屬性只針對Error和RuntimeException

    我在網上找了一個特別合適的類圖來說明:

    四、事務的傳播性在同一個類中方法互調時為什么會失效?


    我們知道,spring的事務都是通過AOP動態代理實現的。如果想要任何一個方法實現事務代理,就必須通過事務代理類去調用,而內部方法調用與事務代理類一點我關系都木有,所以不會生效。下面我舉個例子

    要代理的接口類

    public interface Subject {void methodOne();void methodTwo(); }

    要代理的真實類

    public class RealSubject implements Subject {@Overridepublic void methodOne() {System.out.println("one");this.menthoTwo();}@Overridepublic void methodTwo() {System.out.println("two");} }

    代理類要調用的事務處理器(代理類就是通過它對方法實現的額外處理)

    public class MyInvocationHandler implements InvocationHandler {private Object obj;public MyInvocationHandler(){}public MyInvocationHandler(Object obj){this.obj = obj;}// 調用方法前的額外處理:記錄方法調用開始時間public void startRecordRequestTime(){System.out.println("方法調用開始時間:"+System.currentTimeMillis());}// 調用方法前的額外處理:模擬處理事務的傳播屬性public void processTransaction(){System.out.println("事務處理..........");}// 調用方法后的額外處理:記錄方法調用結束時間public void endRecordRequestTime(){System.out.println("方法調用結束時間:"+System.currentTimeMillis());}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {this.startRecordRequestTime();// 調用處理事務傳播屬性方法this.processTransaction();// 通過反射執行真實的方法method.invoke(obj,args);this.endRecordRequestTime();return null;} }

    使用代理訪問

    public class Test { public static void main(String[] args) {Subject sub = new RealSubject();MyInvocationHandler handler = new MyInvocationHandler(sub);// 利用反射動態生成的代理類Subject proxy=(Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class[]{Subject.class},handler);// 通過代理類訪問方法proxy.methodOne();} }//運行結果為: 方法調用開始時間:1531564567403 事務處理.......... one two 方法調用結束時間:1531564567403 .

    好了,從執行了proxy.methodOne();這句代碼后的結果能看到,當代理類調用了methodOne()后,methodTwo()是在內部調用的,與代理類一點兒關系也沒有,所以methodOne調用內部的methodTwo時,是不會走事務處理代碼的,所以事務傳播屬性也就會失效。
    上面動態生成的代理類大概如下,有興趣的可以了解下,重點是下面的重寫方法調用的MyInvocationHandler 類的invoke方法:

    public class $Proxy0 extends Proxy implements Subject {private static Method m0;private static Method m1;private static Method m2;private static Method m3;private static Method m4;static {try {m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]);m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]);// 方法1m3 = Class.forName("***.RealSubject").getMethod("methodOne",new Class[0]);// 方法2m4 = Class.forName("***.RealSubject").getMethod("methodTwo",new Class[0]);} catch (NoSuchMethodException nosuchmethodexception) {throw new NoSuchMethodError(nosuchmethodexception.getMessage());} catch (ClassNotFoundException classnotfoundexception) {throw new NoClassDefFoundError(classnotfoundexception.getMessage());}}public $Proxy0(InvocationHandler invocationhandler) {super(invocationhandler);}@Overridepublic final int hashCode() {try {return ((Integer) super.h.invoke(this, m0, null)).intValue();} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}@Overridepublic final String toString() {try {return (String) super.h.invoke(this, m2, null);} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}// 重寫Subject接口方法一@Overridepublic final void methodOne() {try {// 調用事務增強處理方法super.h.invoke(this,m3,null);} catch (Throwable throwable) {throwable.printStackTrace();}}// 重寫Subject接口方法二@Overridepublic final void methodTwo() {try {// 調用事務增強處理方法super.h.invoke(this,m4,null);} catch (Throwable throwable) {throwable.printStackTrace();}}}

    總結

    以上是生活随笔為你收集整理的Spring事务的处理流程、传播属性、及部分释疑的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。