tft每日頭條

 > 生活

 > spring事務管理流程

spring事務管理流程

生活 更新时间:2024-07-24 13:19:42

前言

最近在開發業務代碼的時候,犯了一個事務注解的錯誤:在同一個類的非事務方法中調用了另一個事務方法,導緻事務沒有生效,如下所示:

public ConfirmOrderResultVO batchConfirmPurchaseOrders(Long taobaoUserId, List<String> bizOrderIds) throws TCException { ……………………………………………………………… for (String bizOrderId : bizOrderIds) { // 推單成功進入successList,否則進入failedList if (confirmPurchaseOrder(taobaoUserId, bizOrderId)){ successList.add(Long.valueOf(bizOrderId)); }else { failedList.add(Long.valueOf(bizOrderId)); } } ……………………………………………………………… }

其中confirmPurchaseOrder()是一個事務方法:

@Transactional public Boolean confirmPurchaseOrder(Long taobaoUserId, String bizOrderId) throws TCException { logger.warn("|ConfirmPurchaseOrder|UserId:" taobaoUserId ",orderId:" bizOrderId); ………………………… return ture; }

這樣在直接調用batchConfirmPurchaseOrders()方法時,如果confirmPurchaseOrder()方法發生了異常,是不會回滾的。原理在于Spring的事務管理依靠的動态代理模式,當在同一個類中調用一個非事務方法,是不會生成代理對象的,自然也不會觸發事務。借此機會回顧一下代理模式和Spring事務管理的原理。

代理模式

網上講解代理模式的文章千奇百怪,很多時候看完了也不明白講的重點是什麼。

事實上在生活中我們常常會遇到各種各樣的代理模式,例如火車票代售點代理出售火車票,他在“幫忙”出售火車票的同時,收取了額外的手續費,記錄了自己店裡的流水等。

spring事務管理流程(淺入淺出的代理模式與Spring事務管理)1

又比如班長代理老師來上交班費,他在“上交班費”的動作之前,還進行了檢查班級同學是否到齊、向每一位同學收班費、核對班費金額,最後再上交班費。



spring事務管理流程(淺入淺出的代理模式與Spring事務管理)2



總而言之,代理模式就是 代理其他對象,在完成原動作的基礎上(前後)做一些額外的自定義的工作 。

聰明的朋友看到這裡一定想到了:那我在一個方法中調用另一個方法不就是代理模式了嗎?啊對對對,從功能上來講是一樣的,但是“模式”之所以為“模式”,以我拙見,其本質目的在于形成規範,以減少重複代碼的編寫。因此如果不能達到減少代碼重複的本質目的,便不能稱之為“模式”。

按照代理模式的實現方式,分為兩種:靜态代理和動态代理。那我們就拿剛剛說過的“寫作業”這件事來做例子,講解一下兩種實現方式的區别。

▐靜态代理

首先定義一個“作業”接口,裡面有一個方法“做作業”

public interface Homework { void doHomework(); }

小紅實現了這個接口。但是小紅是一個不愛學習的小學生,又因為師從馬掌門成為了學校的扛把子,所以為了不讓老師發現,他決定自己随便做做,把剩下的部分交給小弟們來做。

public class XiaoHong implements Homework{ @Override public void doHomework() { System.out.println("XiaoHong did homework casually"); } }

其中小王、小張兩個小弟成績優異,一個負責數學作業,一個負責英語作業,于是他們兩個自告奮勇實現了Homework接口,在代理完成作業的基礎上,還不斷學習提高自己的能力,并且對作業答案進行了校驗。

小王:

public class XiaoWang implements Homework{ // 持有Homework屬性 private Homework homework; // 通過構造函數初始化Homework public XiaoWang(Homework homework){ this.homework=homework; } //代理實現 @Override public void doHomework() { doStudy(); homework.doHomework(); System.out.println("XiaoWang helps with MathHomework"); doCheck(); } // 額外方法 private void doCheck() { System.out.println("XiaoWang is checking-----------------"); } // 額外方法 private void doStudy() { System.out.println("XiaoWang is studying---------------"); } }

小張:

public class XiaoZhang implements Homework{ // 持有Homework屬性 private Homework homework; // 通過構造函數初始化Homework public XiaoZhang(Homework homework){ this.homework=homework; } //代理實現 @Override public void doHomework() { doStudy(); homework.doHomework(); System.out.println("XiaoZhang helps with EngHomework"); doCheck(); } // 額外方法 private void doCheck() { System.out.println("XiaoZhang is checking-----------------"); } // 額外方法 private void doStudy() { System.out.println("XiaoZhang is studying---------------"); } }

于是,小紅可以放心地把作業交給小王和小張了:

public static void main(String[] args) { // 實例化一個目标對象 XiaoHong xiaoHong = new XiaoHong(); // 把目标對象通過構造函數傳遞給代理對象 XiaoZhang xiaoZhang =new XiaoZhang(xiaoHong); XiaoWang xiaoWang =new XiaoWang(xiaoHong); // 調用代理對象的方法 xiaoZhang.doHomework(); xiaoWang.doHomework(); }

輸出:

XiaoZhang is studying--------------- XiaoHong did homework casually XiaoZhang helps with EngHomework XiaoZhang is checking----------------- XiaoWang is studying--------------- XiaoHong did homework casually XiaoWang helps with MathHomework XiaoWang is checking-----------------

問題來了,如果老師又布置了一個Book接口和readBook方法,但是readBook前後的動作是一樣的(都需要study和check),如何通過代理來實現呢?

一個方案是讓小王和小張都實現Book接口和readBook方法,并持有新的Book對象;另一個方案是再找一個小趙實現Book接口和readBook方法。

這樣兩種方案實際上都能達到效果,但是如果接口和方法增多起來呢?如果讓一個代理類實現一堆方法,并持有一堆不同的對象,這個類勢必會變得臃腫不堪;如果每一個新的方法都創建一個新的代碼類,引用不同的對象,又會造成代碼的大量重複。因此,動态代理就出現了。

▐動态代理

前文所講的靜态代理之所以為“靜态”,是因為每個接口的每個方法,我們都要顯式地去實現、創建、調用,所有的操作都是寫死的、是靜态的。而動态代理的巧妙之處在于,可以在程序的運行期間,動态的生成不同的代理類,來完成相同的工作。也就是說,如果你想實現一個方法,在所有其他方法執行前後做一些額外的相同的動作,例如打印日志、記錄方法執行時間等,你就需要動态代理了(是不是想到了什麼?)。

事實上所有的動态代理思想都是一緻的,而目前最常用的動态代理實現有兩種:JDK原生實現和Cglib開源實現。

Spring在5.X之前默認的動态代理實現一直是JDK動态代理。但是從5.X開始,Spring就開始默認使用Cglib來作為動态代理實現。并且SpringBoot從2.X開始也轉向了Cglib動态代理實現。

  • JDK實現動态代理

現在有一個牛X的機器人代理,可以幫所有人在完成老師布置的任何任務前後,進行學習和答案的核對,在JDK的動态代理實現中,他是這樣編寫的:

// 實現InvocationHandler public class RobotProxy implements InvocationHandler { // 持有一個Object類型的目标對象target private Object target; // 通過構造函數實例化目标對象target public RobotProxy(Object target) { this.target = target; } // 重寫invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doStudy(); // 執行目标對象的方法 Object invoke = method.invoke(target, args); doCheck(); return invoke; } // 額外動作 private void doStudy() { System.out.println("Robot is studying------------"); } // 額外動作 private void doCheck() { System.out.println("Robot is checking------------"); } }

然後這樣調用機器人代理:

public static void main(String[] args) { // 實例化一個目标對象 XiaoHong xiaoHong = new XiaoHong(); // 傳入實現的接口 new Class[]{Homework.class} // 以及代理類 new RobotProxy(xiaoHong) Homework homework = (Homework)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Homework.class}, new RobotProxy(xiaoHong)); homework.doHomework(); }

輸出:

Robot is studying------------ XiaoHong did homework casually Robot is checking------------

可以看到,JDK實現動态代理必須要依賴接口,隻有實現了接口的目标類才可以被代理,而Cglib動态代理就可以消除接口的限制。

  • Cglib實現動态代理

創建一個升級版的機器人RovotV2代理類:

// 實現MethodInterceptor方法(要引入cglib包) public class RobotV2Proxy implements MethodInterceptor { // 重寫intercept方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { doStudy(); Object invoke = methodProxy.invokeSuper(o, objects); doCheck(); return invoke; } // 額外動作 private void doStudy() { System.out.println("RobotV2 is studying------------"); } // 額外動作 private void doCheck() { System.out.println("RobotV2 is checking------------"); } }

調用方式:

public static void main(String[] args) { // 創建增強類 Enhancer enhancer = new Enhancer(); // 設置父類 enhancer.setSuperclass(XiaoHong.class); // 設置回調 enhancer.setCallback(new RobotV2Proxy()); // 創建目标對象 XiaoHong xiaoHong = (XiaoHong)enhancer.create(); // 調用目标方法 xiaoHong.doHomework(); }

輸出:

RobotV2 is studying------------ XiaoHong did homework casually RobotV2 is checking------------

Cglib動态代理相比于JDK代理實現有幾個優勢:

  1. 基于字節碼,生成真實對象的子類。
  2. 運行效率高于JDK代理
  3. 不需要實現接口

可以看到,動态代理隻需要一個代理類,就可以代理所有需要同一種處理(如上文所述的study()、check())的類和方法,這就是動态代理與靜态代理最大的區别。

Spring事務管理

OK,了解到了代理模式的好處之後,就可以自然而然的想到Spring事務管理的基本原理了。無非是借助動态代理,做一些額外的操作:在真正的方法執行之前開啟事務,在方法順利執行完成之後提交事務,如果方法執行過程中發生異常,要執行回滾操作。

一般有兩種方法使用Spring的事務管理,一種是利用注解,一種是手動編程。本文因為使用的是注解型的事務管理,因此隻對注解型事務的使用和原理進行探究,以找到事務沒有生效的原因。

▐注解型事務
  • 注解型事務使用

注解型事務的使用非常簡單,隻需要在需要事務管理的類或方法上加上@Transactional(rollbackFor = Exception.class),其中rollbackFor指定的是遇到什麼情況下執行回滾。

當然這個注解還有傳播級别、隔離級别等我們背過的八股文屬性,就不一一展開了。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved