自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring事務(wù)的一道面試題

開(kāi)發(fā)
每次聊起Spring事務(wù),好像很熟悉,又好像很陌生。本篇通過(guò)一道面試題和一些實(shí)踐,來(lái)拆解幾個(gè)Spring事務(wù)的常見(jiàn)坑點(diǎn)。

每次聊起Spring事務(wù),好像很熟悉,又好像很陌生。本篇通過(guò)一道面試題和一些實(shí)踐,來(lái)拆解幾個(gè)Spring事務(wù)的常見(jiàn)坑點(diǎn)。

原理

Spring事務(wù)的原理是:通過(guò)AOP切面的方式實(shí)現(xiàn)的,也就是通過(guò)代理模式去實(shí)現(xiàn)事務(wù)增強(qiáng)。

具體過(guò)程是:對(duì)包含@Transactional注解的方法進(jìn)行攔截,然后重寫(xiě),重新在方法里加入異?;貪L的邏輯。而且,每個(gè)線程都是獨(dú)立管理自己的事務(wù),相互隔離。

原理簡(jiǎn)單,使用起來(lái)也簡(jiǎn)單,也就是在方法上打上@Transactional注解,然后事務(wù)就正常生效了。也很少有人去驗(yàn)證異常情況下是否能真正的回滾。

Spring事務(wù)讓我熟悉的地方是哪哪看起來(lái)都簡(jiǎn)單,讓我陌生的地方使用時(shí)的變種較多,有時(shí)候莫名其妙的不生效。

1.源碼

以上原理的相關(guān)源碼如下:

2.實(shí)踐出真知

但是 [半支煙] 偶爾會(huì)在編碼過(guò)程中發(fā)現(xiàn)有些場(chǎng)景下的事務(wù)是失效的,總有些情況讓你想不到,總有一些坑點(diǎn)等你去跳。

[半支煙] 覺(jué)得驗(yàn)證事務(wù)的最好方式就是:記住基本原則 + 動(dòng)手實(shí)踐。記住基本原則可以快速處理常規(guī)問(wèn)題,動(dòng)手實(shí)踐可以驗(yàn)證偏門(mén)問(wèn)題或者不確定的問(wèn)題。

幾種事務(wù)不生效的用法

如下是常見(jiàn)的幾種Spring事務(wù)不生效的用法,有空的讀者一定要牢記,對(duì)日常編碼很有幫助,同時(shí)面試時(shí)也能說(shuō)幾句。

1.private方法

Spring是通過(guò)AOP代理的方式實(shí)現(xiàn)事務(wù)增強(qiáng)的,但是private方法無(wú)法被代理,所以在private方法上打@Transactional注解是不生效的。

2.final、static修飾的方法

和private方法類(lèi)似,final和static修飾的方法也無(wú)法被代理,所以@Transactional注解也不生效。

因?yàn)?,static是屬于類(lèi)方法,final修飾的方法無(wú)法被重寫(xiě),自然也就無(wú)法植入事務(wù)增強(qiáng)代碼。

3.Bean對(duì)象沒(méi)有被Spring托管

某個(gè)類(lèi)一定要被Spring托管,那才能通過(guò)@Transactional注解去增強(qiáng)事務(wù)。如果只有@Transactional注解,而沒(méi)有把類(lèi)交給Spring托管,事務(wù)也是不生效的。類(lèi)似如下情況:

// 此處沒(méi)有@Service注解,此類(lèi)不被spring托管,及時(shí)有@Transactional也不生效
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public final void createAndUpdateUser() {
        createUser();
        updateUserById();
    }

    public void createUser() {
        User user = new User();
        user.setId(2L);
        user.setName("test2");
        user.setEmail("test2" + "@test.com");
        userMapper.insert(user);
        System.out.println("create user");
    }

    public void updateUserById() {
        User user = userMapper.findById(1L);
        user.setName("admin1");
        userMapper.update(user);
        int i = 1 / 0; // 此處會(huì)拋出異常
        System.out.println("update user");
    }
}

4.異常被吞掉

如果在業(yè)務(wù)代碼里,通過(guò)try......catch捕獲了異常,同時(shí)又沒(méi)有繼續(xù)拋出異常時(shí),Spring事務(wù)也是不生效的。

因?yàn)榇碓鰪?qiáng)的邏輯就是要發(fā)現(xiàn)了異常,才能回滾事務(wù)。如果異常被方法本身吞掉了,則代理會(huì)認(rèn)為沒(méi)有異常,從而無(wú)法回滾。

5.非RuntimeException異常

Spring事務(wù)默認(rèn)會(huì)回滾RuntimeException 及其子類(lèi),以及 Error 類(lèi)型的異常。如果是其余異常,則不會(huì)回滾。源碼處可見(jiàn):

這種非RuntimeException異常場(chǎng)景下,需要做2個(gè)動(dòng)作從而保證事務(wù)回滾。

  • 捕獲異常,然后拋出自定義異常。
  • 自行在@Transactional注解中增加@Transactional(rollbackFor = XxxxxxxException.class)屬性。或者直接使用rollbackFor = Exception.class,也就免去了第一步。

6.異步線程的場(chǎng)景

多個(gè)線程的場(chǎng)景下,只需要牢記每個(gè)線程只管理自己的事務(wù)即可。每個(gè)線程都有一個(gè)獨(dú)立的事務(wù)上下文,存在ThreadLocal中,所以事務(wù)信息在不同線程之間是隔離的。

7.重災(zāi)區(qū):在同一個(gè)類(lèi)中調(diào)用本類(lèi)的方法

這個(gè)失效場(chǎng)景,是最容易出錯(cuò)的,而且變種還多。在同一個(gè)類(lèi)中調(diào)用本類(lèi)的方法時(shí),牢記以下2點(diǎn),即可破局:

  • 是否會(huì)開(kāi)啟事務(wù)依賴(lài)此類(lèi)的第一個(gè)被外部調(diào)用的方法。如果此類(lèi)的第一個(gè)被外部調(diào)用的方法有@Transactional注解,那事務(wù)生效。
  • 調(diào)用自己內(nèi)部方法時(shí),采用的是this.xxxMethod()的方式,這種方式是不會(huì)走AOP代理的,所以被調(diào)用的內(nèi)部方法的@Transactional注解不生效。

如果確實(shí)需要調(diào)用內(nèi)部方法,并且要事務(wù)生效的話,那只能將被調(diào)用的內(nèi)部方法獨(dú)立到新的類(lèi)中,同時(shí)交給Spring管理。

一道面試題

以上關(guān)于事務(wù)不生效的用法都比較好記,只有在同一個(gè)類(lèi)中調(diào)用本類(lèi)的方法場(chǎng)景下存在多種變種。具體請(qǐng)看這道面試題。請(qǐng)問(wèn)以下createAndUpdateUser方法的事務(wù)生效嗎?

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public final void createAndUpdateUser() { //注意這里有final修飾
        createUser();
        updateUserById();
    }

    @Transactional
    public void createUser() {
        User user = new User();
        user.setId(2L);
        user.setName("test2");
        user.setEmail("test2" + "@test.com");
        userMapper.insert(user);
        System.out.println("create user");
    }


    @Transactional(rollbackFor = Exception.class)
    public void updateUserById() {
        User user = userMapper.findById(1L);
        user.setName("admin1");
        userMapper.update(user);
        int i = 1 / 0; // 此處會(huì)拋出異常
        System.out.println("update user");
    }
}

如果按照重災(zāi)區(qū):在同一個(gè)類(lèi)中調(diào)用本類(lèi)的方法里提到的2個(gè)原則,則事務(wù)全部生效。

如果按照f(shuō)inal、static修飾的方法里提到的原則,則事務(wù)全部不生效。

那結(jié)果如何呢?結(jié)果是以上方法的事務(wù)全部生效。

為什么呢?這里在補(bǔ)充一個(gè)原則:final修飾的方法如果帶上@Transactional注解,事務(wù)情況按照被調(diào)用的方法自身的事務(wù)托管情況而定。

因?yàn)橐陨洗a中的createUser方法和updateUserById方法,都有@Transactional注解,所以都生效。

這種特殊情況也實(shí)在是讓人瞠目,不過(guò)只需要牢記以上幾種不生效的用法即可,誰(shuí)沒(méi)事兒寫(xiě)這種@Transactional + final的代碼呢?除了面試會(huì)問(wèn)......

總結(jié)

本篇主要聊了幾種事務(wù)不生效的用戶(hù),有興趣的讀者可以記一下。同時(shí),還出了一道特殊場(chǎng)景的面試題,供讀者自行實(shí)踐。希望對(duì)你有幫助!

責(zé)任編輯:趙寧寧 來(lái)源: 程序員半支煙
相關(guān)推薦

2011-05-23 11:27:32

面試題面試java

2018-03-06 15:30:47

Java面試題

2009-08-11 10:12:07

C#算法

2023-02-04 18:24:10

SeataJava業(yè)務(wù)

2009-08-11 14:59:57

一道面試題C#算法

2021-05-31 07:55:44

smartRepeatJavaScript函數(shù)

2009-08-11 15:09:44

一道面試題C#算法

2017-11-21 12:15:27

數(shù)據(jù)庫(kù)面試題SQL

2022-04-08 07:52:17

CSS面試題HTML

2023-08-01 08:10:46

內(nèi)存緩存

2021-03-16 05:44:26

JVM面試題運(yùn)行時(shí)數(shù)據(jù)

2021-10-28 11:40:58

回文鏈表面試題數(shù)據(jù)結(jié)構(gòu)

2022-02-08 18:09:20

JS引擎解析器

2011-03-02 10:58:16

SQL server入門(mén)面試題

2015-09-02 14:09:19

面試題程序設(shè)計(jì)

2017-03-10 09:33:16

JavaScript類(lèi)型

2017-09-13 07:15:10

Python讀寫(xiě)文件函數(shù)

2021-03-27 10:59:45

JavaScript開(kāi)發(fā)代碼

2011-06-14 09:12:03

JavaScript

2021-04-13 08:50:21

JS作用域面試題
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)