Spring AOP失效的場景事務失效的場景

場景一:使用this調用被增強的方法

下面是一個類里面的一個增強方法

@Service
public class MyService  implements CommandLineRunner {private  MyService myService;public void performTask(int x) {System.out.println("Executing performTask method");if(x<5) this.performTask(1+x);
//        if(x<5) myService.performTask(1+x);}@Overridepublic void run(String... args) throws Exception {myService = AopTestApplication.getBean(MyService.class);}
}

增強邏輯是在方法調用的前后輸出各一行語句

@Aspect
@Component
public class LoggingAspect {@Pointcut("execution(* com.example.aoptest.MyService.performTask(..))")public void performTaskPointcut() {// Pointcut for performTask method}@Before("performTaskPointcut()")public void beforePerformTask() {System.out.println("方法調用前");}@After("performTaskPointcut()")public void afterPerformTask() {System.out.println("方法調用后");}
}

運行項目并在Controller層調用方法得到的輸出如下

可以看出只有第一次調用是執行了增強邏輯的,剩下那些this調用都沒有。

原因

aop代理的原理:使用AOP對某一個Bean的方法進行增強之后,放進IOC容器的這個Bean不會是原本的類實例,而是專門創建的一個代理對象Bean.這個代理對象內部有對原始Bean的引用,在用原始Bean調用方法前會先執行增強邏輯。這里就是相當于加了一層。

用this.失效的原因: 用this.xxx相當于直接調用了原始Bean的方法,而不是外層的代理Bean的方法。示意圖如下,左邊的是用代理對象調用方法,右邊的是在原始Bean的任意方法內部用this.調用增強方法。

解決方案

將this.調用改為Bean調用

代碼如下。

必須從IOC容器里面直接獲取到Bean對象,而不是使用@Autowried注解注入,否則會出現循環依賴的報錯。

@SpringBootApplication
public class AopTestApplication implements ApplicationContextAware {private static ApplicationContext context;public static void main(String[] args) {SpringApplication.run(AopTestApplication.class, args);}public static <T> T getBean(Class<T> beanClass) {return context.getBean(beanClass);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}
}
@Service
public class MyService  implements CommandLineRunner {private  MyService myService;public void performTask(int x) {System.out.println("Executing performTask method");if(x<5) myService.performTask(1+x);}@Overridepublic void run(String... args) throws Exception {myService = AopTestApplication.getBean(MyService.class);}
}

這次的輸出如下,可以看見每一次方法調用都有執行增強邏輯。

場景二:增強方法使用private修飾

@RestController
@Service
public class MyService   implements CommandLineRunner  {private  MyService myService;@PostMapping("test/{x}")private void performTask(@PathVariable("x")int x) {System.out.println("Executing performTask method");if(Objects.isNull(myService))System.out.println("222222");if(x<5) myService.performTask(1+x);}@Overridepublic void run(String... args) throws Exception {myService = AopTestApplication.getBean(MyService.class);if(Objects.isNull(myService))System.out.println("1111111");}
}

上面代碼的輸出如下,可以看見并沒有輸出增強邏輯。并且在使用myService調用方法時還報錯myService為空。明明run方法里面都已經注入了Bean.

原因

aop代理無法代理私有方法?代理對象無法通過原始Bean調用里面的私有方法?

同一個類里面存在一個私有和公有的兩個增強方法時如下

@RestController
@Service
public class MyService   implements CommandLineRunner  {private  MyService myService;@PostMapping("test/{x}")private void performTask(@PathVariable("x")int x) {System.out.println("Executing performTask method");if(Objects.isNull(myService))System.out.println("222222");if(x<5) myService.performTask(1+x);}@PostMapping("test2/{x}")public void performTask2(@PathVariable("x")int x) {System.out.println("Executing performTask method2");if(Objects.isNull(myService))System.out.println("222222");if(x<5) myService.performTask2(1+x);}@Overridepublic void run(String... args) throws Exception {myService = AopTestApplication.getBean(MyService.class);if(Objects.isNull(myService))System.out.println("1111111");}
}

先后調用方法2和方法1得到輸出如下

結果是方法2一切正常,方法1還是有問題。

?奇怪的點是方法2里面測出局部變量myService是非空的,但是方法1里面卻是空的????

解決方案

將private改成public,現在可以看見增強邏輯都正常輸出了。

AOP失效會導致的問題

估計所有依賴于AOP實現的功能都會有問題

首當其沖的就是事務注解@Transactional,事務注解里面也是依賴了AOp,在方法調用前開啟事務,在方法調用后進行回滾或者提交事務。

但是像上面失效的場景下就事務注解就會失效,試想,在一個方法內用this調用了同一個類中的一個事務方法,那這個事務方法的事務就會失效,里面的事務方法報錯回滾會無法回滾。

事務失效場景一:拋出檢查異常checked

默認情況下,Spring事務管理器只在拋出未檢查(unchecked)異常(即RuntimeException及其子類)時才會回滾事務。如果方法拋出的是檢查(checked)異常,事務不會回滾,除非你顯式配置了事務注解的rollbackFor屬性。

如下所示,我在業務代碼中手動拋出了一個自定義的檢查異常,報錯是報錯了,但是事務并沒有回滾。


@Service
public class UserServiceImpl implements UserService , CommandLineRunner {public class MyCheckedException extends Exception {public MyCheckedException(String message) {super(message);}}private UserMapper userMapper;private UserService userService;@Override@Transactionalpublic void createUser(String name) throws MyCheckedException{user user = new user();user.setName(name);userMapper.insert(user);throw new MyCheckedException("This is a custom checked exception");}@Overridepublic void run(String... args) throws Exception {userMapper = AopTestApplication.getBean(UserMapper.class);userService = AopTestApplication.getBean(UserService.class);}
}

原因

spring事務默認是只在拋出未檢查異常時才會進行回滾。

解決方案

顯式配置rollback屬性

在上面代碼的事務注解改成如下,這樣子無論是什么異常都會自動回滾了

    @Transactional(rollbackFor = Exception.class)

當然,還有一個屬性選擇是Throwable,?這個是Exception和Error的父類,這樣不管是拋出異常還是錯誤都會進行回滾了,正常來想,應該也是要這樣設置的。

    @Transactional(rollbackFor = Throwable.class)

事務失效場景二:捕獲異常之后不拋出

    @Override@Transactional(rollbackFor = Exception.class)public void createUser(String name) throws MyCheckedException{user user = new user();user.setName(name);userMapper.insert(user);try{throw new MyCheckedException("This is a custom checked exception");}catch(Exception ex){}}

如上所示,沒有將異常拋出就不會觸發回滾。

解決方案

記得拋出異常

    @Override@Transactional(rollbackFor = Exception.class)public void createUser(String name) throws MyCheckedException{user user = new user();user.setName(name);userMapper.insert(user);try{throw new MyCheckedException("This is a custom checked exception");}catch(Exception ex){throw  ex;}}

事務失效場景三:使用了非public方法

Spring的事務管理依賴于AOP代理,而AOP只能代理public方法。如果你在非public方法上使用了@Transactional注解,這個注解將不起作用,事務也不會生效。

原理和上面aop失效的原理一樣。

事務失效場景四:內部方法調用

如果在同一個類中,一個方法調用了另一個帶有@Transactional注解的方法,Spring的AOP代理將無法攔截這個內部調用,導致事務注解失效。解決方法之一是將被調用的方法提取到另一個bean中。

原理也和上面aop使用this調用失效的原理一樣。

代碼如下:

一個普通方法調用了一個事務方法,并且事務方法里面手動拋出異常,正常來說應該回滾的,但是因為是this.調用,aop失效就無法回滾了。

@Service
public class UserServiceImpl implements UserService , CommandLineRunner {public class MyCheckedException extends Exception {public MyCheckedException(String message) {super(message);}}private UserMapper userMapper;private UserService userService;@Overridepublic void createUser(String name) throws MyCheckedException {user user = new user();user.setName(name);
//        userMapper.insert(user);this.test();}@Transactional(rollbackFor = Exception.class)public void test() throws MyCheckedException {user user = new user();user.setName(new Date().toString());userMapper.insert(user);try{throw new MyCheckedException("This is a custom checked exception");}catch(Exception ex){throw  ex;}}@Overridepublic void run(String... args) throws Exception {userMapper = AopTestApplication.getBean(UserMapper.class);userService = AopTestApplication.getBean(UserService.class);}
}

解決方案一

將this.調用換成Bean調用

解決方案二

使用事務方法調用事務方法,這樣子即使是this調用也會回滾了。

代碼如下所示,但是這個的本質是外層事務方法進行的回滾,里面調用的事務方法并沒有執行回滾。

    @Override@Transactional(rollbackFor = Exception.class)public void createUser(String name) throws MyCheckedException {user user = new user();user.setName(name);
//        userMapper.insert(user);this.test();}@Transactional(rollbackFor = Exception.class)public void test() throws MyCheckedException {user user = new user();user.setName(new Date().toString());userMapper.insert(user);try{throw new MyCheckedException("This is a custom checked exception");}catch(Exception ex){throw  ex;}}

事務失效場景五:事務傳播屬性設置不當

事務失效場景六:不同的事務管理器

如果應用中有多個數據源,可能會有多個事務管理器。如果在配置事務管理器時沒有指定正確的事務管理器,可能會導致事務失效。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/16940.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/16940.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/16940.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

爬蟲學習--15.進程與線程(2)

線程鎖 當多個線程幾乎同時修改某一個共享數據的時候&#xff0c;需要進行同步控制 某個線程要更改共享數據時&#xff0c;先將其鎖定&#xff0c;此時資源的狀態為"鎖定",其他線程不能改變&#xff0c;只到該線程釋放資源&#xff0c;將資源的狀態變成"非鎖定…

Linux如何設置共享文件夾

打開虛擬機->菜單->虛擬機設置->選項->共享文件夾->總是啟用。點擊添加按鈕->彈出添加向導->點擊瀏覽按鈕&#xff0c;從windows中選擇一個文件夾&#xff0c;確定即可。

[Windows] GIF動畫、動圖制作神器 ScreenToGif(免費)

ScreenToGif 是開源免費的 Gif 動畫錄制工具&#xff0c;小巧原生單文件&#xff0c;功能很實用。它有錄制屏幕、錄制攝像頭、錄制畫板、圖像編輯器等功能&#xff0c;可以將屏幕任何區域及操作過程錄制成 GIF 格式的動態圖像。保存前還可對 GIF 圖像編輯優化&#xff0c;支持自…

末日設計1.00

故事背景: 在不遠的未來&#xff0c;世界陷入了末日危機。資源枯竭、社會秩序崩潰&#xff0c;幸存者們為了生存&#xff0c;不得不拿起武器爭奪每一寸土地和每一口食物。在這個混亂的世界中&#xff0c;你是一名傳奇狙擊手&#xff0c;憑借超凡的射擊技巧和生存智慧&#xff0…

研二學妹面試字節,竟倒在了ThreadLocal上,這是不要應屆生還是不要女生啊?

一、寫在開頭 今天和一個之前研二的學妹聊天&#xff0c;聊及她上周面試字節的情況&#xff0c;著實感受到了Java后端現在找工作的壓力啊&#xff0c;記得在18&#xff0c;19年的時候&#xff0c;研究生計算機專業的學生&#xff0c;背背八股文找個Java開發工作毫無問題&#x…

本地圖形客戶端查看git提交歷史 使用 TortoiseGit

要在本地查看提交記錄和修改歷史&#xff0c;可以使用 TortoiseGit 和 Git-SCM。這兩個工具都提供了強大的功能來管理和查看 Git 倉庫中的提交記錄和歷史修改。 使用 TortoiseGit 查看提交記錄和修改歷史 查看提交記錄&#xff08;Log&#xff09;&#xff1a; 右鍵點擊項目文…

抖音里賣什么最賺錢?4個冷門的高利潤商品,還有誰不知道!

哈嘍~我的電商月月 做抖音小店的新手朋友&#xff0c;一定很想知道&#xff0c;在抖音里賣什么最賺錢&#xff1f; 很多人都會推薦&#xff0c;日常百貨&#xff0c;小風扇&#xff0c;女裝&#xff0c;寵物用品等等&#xff0c;這些商品確實很好做&#xff0c;你們可以試試 …

Euraka詳解:實現微服務架構的關鍵組件

在當今互聯網時代&#xff0c;微服務架構已經成為許多企業構建和部署應用程序的首選方法之一。而要在微服務架構中實現高可用性和靈活性&#xff0c;服務發現和注冊是至關重要的一環。Eureka作為Netflix開源的服務發現組件&#xff0c;為實現這一目標提供了高效可靠的解決方案。…

備忘錄可以統計字數嗎?備忘錄里在哪查看字數?

在這個信息爆炸的時代&#xff0c;很多人喜歡使用備忘錄app來記錄生活中的點點滴滴。備忘錄不僅可以幫助我們記事、安排日程&#xff0c;還能提醒我們完成各種任務&#xff0c;是我們日常生活中不可或缺的小助手。 然而&#xff0c;在使用備忘錄時&#xff0c;有時我們會遇到需…

不用BookStack的企業都在用什么知識庫軟件

現如今&#xff0c;越來越多的企業使用知識庫軟件對企業內部知識進行管理。BookStack作為一款功能強大的開源知識庫軟件&#xff0c;成為很多企業的首選。但是還是有一部分人群認為BookStack不適合他們的企業那么他們都是在用什么別的知識庫軟件呢&#xff1f;LookLook同學今天…

《python本機環境多版本切換》-兩種方式以及具體使用--venv/pyenv+pycharm測試

阿丹&#xff1a; source myenv/bin/activate 在開發使用rasa的時候發現自己安裝的python環境是3.12的&#xff0c;和rasa不兼容&#xff0c;所以實踐一下更換多python環境。 使用虛擬環境 在Python中使用虛擬環境來切換Python版本是一個常見的做法&#xff0c;這可以幫助你…

Minikube部署單節點Kubernetes

1.1 Minikube部署單節點K8s Minikube是由Kubernetes社區維護的單機版的Kubernetes集群&#xff0c;支持macOS, Linux, andWindows等多種操作系統平臺&#xff0c;使用最新的官方stable版本&#xff0c;并支持Kubernetes的大部分功能&#xff0c;從基礎的容器編排管理&#xff0…

實用篇| huggingface網絡不通

之前文章《Transformer原理》中介紹過,Transformers 是由 Hugging Face 開發的一個包&#xff0c;支持加載目前絕大部分的預訓練模型。隨著 BERT、GPT 等大規模語言模型的興起&#xff0c;越來越多的公司和研究者采用 Transformers 庫來構建應用。 Hugging Face是一家美國公司…

Easy IP + DNAT(服務器NAT轉換)

第一章 Easy IP 1.1 一般家庭和企業使用的地址轉換方式 直接使用出接口的地址做轉換Easy IP適用于小規模居于網中的主機訪問Internet的場景如&#xff1a;家庭、小型網吧、小型辦公室中&#xff0c;這些地方內部主機不多&#xff0c;出接口可以通過撥號方式獲取一個臨時公網I…

2.Nginx上配置圖片訪問

在 Nginx 上配置圖片訪問涉及到在 Nginx 配置文件中添加相應的 location 塊來處理圖片請求。以下是一個基本的示例&#xff0c;演示如何配置 Nginx 以便在指定目錄中存儲和訪問圖片。 1.上傳圖片到服務器 首先&#xff0c;將你的圖片上傳到服務器的某個目錄&#xff0c;例如 …

視頻監控匯聚平臺LntonCVS通過GB/T28181國標協議實現視頻監控平臺的級聯方案

近年來&#xff0c;隨著網絡視頻監控應用范圍的拓展&#xff0c;越來越多的政府部門和跨區域行業單位對視頻監控的需求已經不局限于本地聯網監控。他們正在探索在原有的本地聯網監控基礎上&#xff0c;建設省級乃至全國范圍內的跨區域監控聯網&#xff0c;以全面打造數據共享平…

BUUCTF靶場[Reverse]內涵的文件、新年快樂

[reverse]內涵的文件 文件運行看一下 老規矩&#xff0c;拿到文件先用DIE查有沒有殼 沒有殼&#xff0c;且是一個32位的文件&#xff0c;用相對應的IDA打開 &#xff0c;有主函數&#xff08;mian&#xff09;&#xff0c;先點開 這里點開&#xff08;mian_0&#xff09;,發現…

Kotlin基礎之基本語法

Kotlin 簡介 Kotlin 是一種由 JetBrains 開發的靜態類型編程語言&#xff0c;設計用于與 Java 虛擬機 (JVM) 兼容&#xff0c;同時也可用于 Android、JavaScript&#xff08;通過 Kotlin/JS&#xff09;和原生&#xff08;通過 Kotlin/Native&#xff09;開發。Kotlin 旨在提供…

【詳細介紹WebKit的結構】

&#x1f3a5;博主&#xff1a;程序員不想YY啊 &#x1f4ab;CSDN優質創作者&#xff0c;CSDN實力新星&#xff0c;CSDN博客專家 &#x1f917;點贊&#x1f388;收藏?再看&#x1f4ab;養成習慣 ?希望本文對您有所裨益&#xff0c;如有不足之處&#xff0c;歡迎在評論區提出…

springboot + es7.12.3 elasticsearchRestTemplate使用記錄

private BoolQueryBuilder getQueryBuilder(QueryCollectWaterDataPageRequestVO requestVO) {BoolQueryBuilder queryBuilder QueryBuilders.boolQuery();if (!CollectionUtils.isEmpty(requestVO.getCompanyIds())) {//termsQuery 精確查找corpId字段為精確的多個值&#xf…