最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java的@Transactional、@Aysnc、事務(wù)同步問題詳解

 更新時間:2023年11月27日 08:47:00   作者:ps酷教程  
這篇文章主要介紹了Java的@Transactional、@Aysnc、事務(wù)同步問題詳解,現(xiàn)在我們需要在一個業(yè)務(wù)方法中插入一個用戶,這個業(yè)務(wù)方法我們需要加上事務(wù),然后插入用戶后,我們要異步的方式打印出數(shù)據(jù)庫中所有存在的用戶,需要的朋友可以參考下

場景

我們要做的事情很簡單:

  1. 現(xiàn)在我們需要在一個業(yè)務(wù)方法中插入一個用戶,
  2. 這個業(yè)務(wù)方法我們需要加上事務(wù),
  3. 然后插入用戶后,我們要異步的方式打印出數(shù)據(jù)庫中所有存在的用戶。

最初版本

我們的代碼在最開始,可能是如下:

TestController

@RestController
@RequestMapping("test")
public class TestController {
    @Autowired
    private TestService testService;
    @GetMapping("testTx")
    public String testTx() {
        testService.doTx();
        return "ok";
    }
}

TestService

@Slf4j
@Service
@EnableAsync // 開啟異步
@EnableTransactionManagement // 開啟事務(wù)
public class TestService {

    @Autowired
    private UserService userService;

    @Transactional
    public void doTx(){
        log.info("-----------------doTx-----------------" + this.getClass());

        User user = new User();
        user.setNickname(RandomStringUtils.randomAlphabetic(5));

        userService.save(user); // 插入用戶
        log.info("插入用戶:{}" , user);

        printUserList(); // 我們希望的是異步打印所有的用戶

        log.info("-----------------doTx-----------------");

        try {
            Thread.sleep(3000); // 這里還需要干其它的活,反正就是這里不確定,萬一它就卡在這里了呢, 就模擬這個情況
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Async
    public void printUserList() {
        log.info("-----------------printUserList-----------------" + this.getClass());
        List<User> list = userService.list(new QueryWrapper<User>());
        for (User user1 : list) {
            log.info("printUser:  {}",user1);
        }
        log.info("-----------------printUserList-----------------");

    }

}

問題

我們訪問上面的這個接口://localhost:8085/web-api/test/testTx,輸出如下的日志。

發(fā)現(xiàn)問題:可以看到 保存用戶 和 異步打印所有用戶 用的是同一個線程,說好的異步?jīng)]有了,為什么沒有異步了呢?可以看到我們使用的仍然是TestService而不是代理對象,所以直接就是調(diào)用的就是TestService類的方法,而異步注解是基于代理的(但不是基于自動代理創(chuàng)建器的),所以就有問題了。

在這里插入圖片描述

@Lazy版本 + 事務(wù)同步

既然,上面我們知道了,是由于沒有調(diào)用代理,所以異步打印所有用戶仍然用的是原來的線程。那么再問一句:TestService沒有被代理嗎?它的的確確被代理了,是因為@Transactional讓它做了事務(wù)代理,但是事務(wù)代理基于的就是aop,aop責(zé)任鏈調(diào)用的最終節(jié)點,調(diào)用的是真實對象,所以那里就用的是真實對象去打印,那可不就沒代理了嘛!

原因,我們也知道了,那我們可以讓它自己注入自己,發(fā)現(xiàn)啟動報錯,啟動報錯的原因在于@Async實現(xiàn)代理的方式 和 aop的自動代理方式 用的不是同一個代理創(chuàng)建器。在一般情況下,自己注入自己的確是可以解決這種循環(huán)依賴 + 自動代理的問題的(或者用AopContxt.currentProxy()獲取到綁定到當(dāng)前線程的代理對象),但是一旦碰到這種@Async 和 aop自動代理的情況,由于有2個代理創(chuàng)建器存在,且它們都要對這個對象進(jìn)行代理,那就有問題了。會報如下的錯誤:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService': Bean with name 'testService' has been injected into other beans [testService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 19 common frames omitted

報錯版本:TestService

@Slf4j
@Service
@EnableAsync // 開啟異步
@EnableTransactionManagement // 開啟事務(wù)
public class TestService {

    @Autowired
    private UserService userService;

    @Autowired
    private TestService testService;

    @Transactional
    public void doTx(){
        log.info("-----------------doTx-----------------" + this.getClass());

        User user = new User();
        user.setNickname(RandomStringUtils.randomAlphabetic(5));

        userService.save(user); // 插入用戶
        log.info("插入用戶:{}" , user);

        testService.printUserList(); // 我們希望的是異步打印所有的用戶

        log.info("-----------------doTx-----------------");

        try {
            Thread.sleep(3000); // 這里還需要干其它的活,反正就是這里不確定,萬一它就卡在這里了呢, 就模擬這個情況
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Async
    public void printUserList() {
        log.info("-----------------printUserList-----------------" + this.getClass());
        List<User> list = userService.list(new QueryWrapper<User>());
        for (User user1 : list) {
            log.info("printUser:  {}",user1);
        }
        log.info("-----------------printUserList-----------------");

    }

}

@Lazy正常啟動版本(有問題)

給TestService加個@Lazy注解,就可以解決這個問題,解決的方式是因為在解析含有@Lazy注解的依賴時,會創(chuàng)建一個代理對象,這個代理把從spring容器中獲取目標(biāo)bean的時機(jī),調(diào)整到了使用它的時候,也就是說,往TestService中注入的testService,在解析依賴的解決,不去容器中去找或者創(chuàng)建,而是直接構(gòu)建了個代理對象,放入到里面。這樣就相當(dāng)于沒有發(fā)生循環(huán)發(fā)生一樣,因為循環(huán)依賴產(chǎn)生的的時機(jī)就是在解析bean的依賴的時候,通過@Lazy創(chuàng)建代理的方式處理了依賴,也就不存在這個循環(huán)依賴的問題了。

也好比說:我在TestService中注入一個容器中壓根就沒有定義的bean,但是我給這個這個字段上的bean加了@Lazy注解,它依然可以正常啟動,當(dāng)然,在用的時候,它仍然會報錯。但在這里沒關(guān)系,在啟動階段已經(jīng)不報錯了,在運行階段,會去容器中尋找testService,而在運行階段,spring容器已經(jīng)初始化好了,也就沒問題了。

@Slf4j
@Service
@EnableAsync // 開啟異步
@EnableTransactionManagement // 開啟事務(wù)
public class TestService {
    @Autowired
    private UserService userService;
    @Autowired
    @Lazy
    private TestService testService;
    @Transactional
    public void doTx(){
        log.info("-----------------doTx-----------------" + this.getClass());
        User user = new User();
        user.setNickname(RandomStringUtils.randomAlphabetic(5));
        userService.save(user); // 插入用戶
        log.info("插入用戶:{}" , user);
        testService.printUserList(); // 我們希望的是異步打印所有的用戶
        log.info("-----------------doTx-----------------");
        try {
            Thread.sleep(3000); // 這里還需要干其它的活,反正就是這里不確定,萬一它就卡在這里了呢, 就模擬這個情況
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Async
    public void printUserList() {
        log.info("-----------------printUserList-----------------" + this.getClass());
        List<User> list = userService.list(new QueryWrapper<User>());
        for (User user1 : list) {
            log.info("printUser:  {}",user1);
        }
        log.info("-----------------printUserList-----------------");
    }
}

我們繼續(xù)訪問上面的這個接口://localhost:8085/web-api/test/testTx,輸出如下的日志。

異步打印的問題是解決了,但是,又有個問題了,查出來怎么只會有1個用戶呢?這個接口調(diào)用了2次,肯定會有2個用戶的,現(xiàn)在卻只有一個用戶,原因就在于是異步打印的,當(dāng)前事務(wù)還有提交,然后就去查詢,肯定就只會查詢1個出來。

在這里插入圖片描述

@Lazy + 注冊事務(wù)同步

上面代碼中,調(diào)用@Aysnc注解修飾的異步方法應(yīng)該是要在事務(wù)提交了之后,再去調(diào)用,而不是插入數(shù)據(jù)之后調(diào)用!

所以需要注冊事務(wù)同步到事務(wù)同步管理器中,在事務(wù)提交之后,再去作異步任務(wù),這樣異步任務(wù)才能在數(shù)據(jù)庫中查到剛剛插入的數(shù)據(jù)。感覺有點像vue里面的nextTick了。

@Slf4j
@Service
@EnableAsync // 開啟異步
@EnableTransactionManagement // 開啟事務(wù)
public class TestService {
    @Autowired
    private UserService userService;
    @Autowired
    @Lazy
    private TestService testService;
    @Transactional
    public void doTx(){
        log.info("-----------------doTx-----------------" + this.getClass());
        User user = new User();
        user.setNickname(RandomStringUtils.randomAlphabetic(5));
        userService.save(user); // 插入用戶
        log.info("插入用戶:{}" , user);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                testService.printUserList();// 我們希望的是異步打印所有的用戶
            }
        });
        log.info("-----------------doTx-----------------");
        try {
            Thread.sleep(3000); // 這里還需要干其它的活,反正就是這里不確定,萬一它就卡在這里了呢, 就模擬這個情況
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Async
    public void printUserList() {
        log.info("-----------------printUserList-----------------" + this.getClass());
        List<User> list = userService.list(new QueryWrapper<User>());
        for (User user1 : list) {
            log.info("printUser:  {}",user1);
        }
        log.info("-----------------printUserList-----------------");
    }
}

可以看到,剛剛插入的時id為4用戶,現(xiàn)在能夠把剛剛插入的查詢出來了

在這里插入圖片描述

到此這篇關(guān)于Java的@Transactional、@Aysnc、事務(wù)同步問題詳解的文章就介紹到這了,更多相關(guān)@Transactional、@Aysnc、事務(wù)同步問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java當(dāng)中讓事務(wù)回滾的幾種方式

    Java當(dāng)中讓事務(wù)回滾的幾種方式

    這篇文章主要給大家介紹了關(guān)于Java當(dāng)中讓事務(wù)回滾的幾種方式, 事務(wù)回滾通常用于在某些操作失敗時取消之前已執(zhí)行的所有操作,這樣,我們就可以保證數(shù)據(jù)的一致性,需要的朋友可以參考下
    2023-08-08
  • 解決Mybatis-plus和pagehelper依賴沖突的方法示例

    解決Mybatis-plus和pagehelper依賴沖突的方法示例

    這篇文章主要介紹了解決Mybatis-plus和pagehelper依賴沖突的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Spring組件初始化擴(kuò)展點BeanPostProcessor的作用詳解

    Spring組件初始化擴(kuò)展點BeanPostProcessor的作用詳解

    本文通過實戰(zhàn)案例和常見應(yīng)用場景詳細(xì)介紹了BeanPostProcessor的使用,并強(qiáng)調(diào)了其在Spring擴(kuò)展中的重要性,感興趣的朋友一起看看吧
    2025-03-03
  • Springboot項目快速實現(xiàn)Aop功能

    Springboot項目快速實現(xiàn)Aop功能

    這篇文章主要介紹了Springboot項目如何快速實現(xiàn)Aop功能,對此方面感興趣的小伙伴可以詳細(xì)參考閱讀本文,本文有一定的參考價值
    2023-03-03
  • Springboot的yml配置文件用法

    Springboot的yml配置文件用法

    這篇文章主要介紹了Springboot的yml配置文件用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳實例代碼

    Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳實例代碼

    這篇文章主要介紹了Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳 的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • Java基礎(chǔ)之finally語句與return語句詳解

    Java基礎(chǔ)之finally語句與return語句詳解

    這篇文章主要介紹了Java基礎(chǔ)之finally語句與return語句詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • java字符串的替換replace、replaceAll、replaceFirst的區(qū)別說明

    java字符串的替換replace、replaceAll、replaceFirst的區(qū)別說明

    這篇文章主要介紹了java字符串的替換replace、replaceAll、replaceFirst的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java中ArrayList的使用方法簡單介紹

    Java中ArrayList的使用方法簡單介紹

    這篇文章主要為大家簡單介紹了Java中ArrayList的使用方法,針對ArrayList去重問題進(jìn)行擴(kuò)展分析,感興趣的小伙伴們可以參考一下
    2016-07-07
  • spring?jpa設(shè)置多個主鍵遇到的小坑及解決

    spring?jpa設(shè)置多個主鍵遇到的小坑及解決

    這篇文章主要介紹了spring?jpa設(shè)置多個主鍵遇到的小坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評論

项城市| 朝阳区| 鸡泽县| 喜德县| 香河县| 白沙| 塔河县| 巴彦县| 梨树县| 甘德县| 绥宁县| 泾源县| 彭山县| 苏尼特右旗| 潼关县| 龙川县| 南和县| 修武县| 罗平县| 富裕县| 云安县| 大埔区| 长海县| 米易县| 山阴县| 梨树县| 和林格尔县| 耒阳市| 香格里拉县| 沙湾县| 山丹县| 西和县| 卓尼县| 揭西县| 建阳市| 甘南县| 兴海县| 天水市| 武功县| 嵊州市| 花莲市|