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

詳解全局事務(wù)注解@GlobalTransactional的識(shí)別

 更新時(shí)間:2022年12月26日 09:47:30   作者:Applehope  
這篇文章主要為大家介紹了詳解全局事務(wù)注解@GlobalTransactional的識(shí)別源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、聲明式全局事務(wù)

Seata示例工程中,能看到@GlobalTransactional,如下方法示例:

@GlobalTransactional
public boolean purchase(long accountId, long stockId, long quantity) {
    String xid = RootContext.getXID();
    LOGGER.info("New Transaction Begins: " + xid);
    boolean stockResult = reduceAccount(accountId,stockId, quantity);
    if (!stockResult) {
        throw new RuntimeException("賬號(hào)服務(wù)調(diào)用失敗,事務(wù)回滾!");
    }
    Long orderId = createOrder(accountId, stockId, quantity);
    if (orderId == null || orderId <= 0) {
        throw new RuntimeException("訂單服務(wù)調(diào)用失敗,事務(wù)回滾!");
    }
    return true;
}

purchase方法上加上此注解,即表示此方法內(nèi)的reduceAccountcreateOrder兩個(gè)微服務(wù)調(diào)用也將加入到分布式事務(wù)中,即扣除賬戶余額與創(chuàng)建訂單將具有分布式事務(wù)的數(shù)據(jù)一致性保障能力。

了解 Spring 注解事務(wù)實(shí)現(xiàn)的話,應(yīng)該也能推測(cè)出,Seata 的事務(wù)能力也可能是基于 Spring 的 AOP 機(jī)制,給標(biāo)注了@GlobalTransactional 的方法做 AOP 增加,織入額外的邏輯以完成分布式事務(wù)的能力,偽代碼大致如下:

GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
    tx.begin(xxx);
    ...
    purchase(xxx)//給purchase增加全局事務(wù)處理能力
    ...
    tx.commit();
} catch (Exception exx) {
    tx.rollback();
    throw exx;
}

二、@GlobalTransactional 注解如何被識(shí)別?

2.1 運(yùn)行環(huán)境

1)引入seata-spring-boot-starter模塊

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>

spring.factories 中有自動(dòng)裝配類SeataAutoConfiguration

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
io.seata.spring.boot.autoconfigure.SeataAutoConfiguration
...

此類負(fù)責(zé)處理全局事務(wù)掃描及設(shè)置,其中就有@GlobalTransactional

2)條件要求:

@ConditionalOnProperty(prefix = SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter({SeataCoreAutoConfiguration.class})
public class SeataAutoConfiguration

從類的自動(dòng)裝備條件,可看出要滿足 2 個(gè)條件:

  • @ConditionalOnProperty,表明若要生效須具備以下配置條件:
    seata.enabled = true
  • @AutoConfigureAfter,表示從 bean 的加載順序來看,要求是在SeataCoreAutoConfiguration之后,SeataCoreAutoConfiguration又是什么呢?
@ConditionalOnProperty(prefix = SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
@ComponentScan(basePackages = "io.seata.spring.boot.autoconfigure.properties")
@Configuration(proxyBeanMethods = false)
public class SeataCoreAutoConfiguration {
    @Bean(BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER)
    @ConditionalOnMissingBean(name = {BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER})
    public SpringApplicationContextProvider springApplicationContextProvider() {
        return new SpringApplicationContextProvider();
    }
}

從源碼可知,也很清晰,大概有幾個(gè)功能點(diǎn):

  • 同樣要求seata.enabled = true
  • 另外會(huì)掃描"io.seata.spring.boot.autoconfigure.properties"
  • 會(huì)提供一個(gè)SpringApplicationContextProvider,基于 Spring 的程序中,很常見這種方式,用于提供 ApplicationContext,通常用于方便獲取 bean 和添加 bean 等。
public class SpringApplicationContextProvider implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.setProperty("file.listener.enabled", "false");
        ObjectHolder.INSTANCE.setObject(OBJECT_KEY_SPRING_APPLICATION_CONTEXT, applicationContext);
    }
}

2.2 功能-注入事務(wù)執(zhí)行失敗處理器FailureHandler

注入一個(gè)FailureHandler,其默認(rèn)實(shí)現(xiàn)DefaultFailureHandlerImpl中只會(huì)打印錯(cuò)誤日志,建議重寫,異常發(fā)生時(shí)及時(shí)告知使用者。

2.3 功能-構(gòu)建全局事務(wù)掃描器GlobalTransactionScanner

構(gòu)建全局事務(wù)掃描器GlobalTransactionScanner,注入到容器中,其內(nèi)部做 2 件事情

  • 會(huì)初始化 TM、RM 客戶端
  • 掃描Bean,對(duì)添加了全局事務(wù)注解的類(@GlobalTransactional、@GlobalLock、@TwoPhaseBusinessAction)生成代理對(duì)象,做AOP增強(qiáng),添加對(duì)應(yīng)的攔截器(攔截器內(nèi)補(bǔ)充分布式事務(wù)的能力)
public class GlobalTransactionScanner extends AbstractAutoProxyCreator

Spring 中 Bean 的關(guān)鍵初始化過程:

實(shí)例化 -> 屬性注入 -> postProcessBeforeInitialization -> afterPropertiesSet/init 方法 -> postProcessAfterInitialization

從以下堆??梢钥闯觯?code>AbstractAutoProxyCreator在 bean 初始化完成之后創(chuàng)建它的代理 AOP 代理,通過wrapIfNecessary判斷是否該 bean 是否存在全局事務(wù)注解,如果有則需要增強(qiáng),添加相應(yīng)的攔截器。

wrapIfNecessary:269, GlobalTransactionScanner (io.seata.spring.annotation)
postProcessAfterInitialization:293, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
applyBeanPostProcessorsAfterInitialization:455, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:1808, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:620, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

先看一個(gè)關(guān)鍵方法existsAnnotation,當(dāng)掃描到 bean 之后,獲取 bean 的原始類型,然后通過此方法原始類型的類或者方法中是否有@GlobalTransactional@GlobalLock注解

private boolean existsAnnotation(Class&lt;?&gt;[] classes) {
    if (CollectionUtils.isNotEmpty(classes)) {
        for (Class&lt;?&gt; clazz : classes) {
            if (clazz == null) {
                continue;
            }
            //判斷類上是否有注解@GlobalTransactional
            GlobalTransactional trxAnno = clazz.getAnnotation(GlobalTransactional.class);
            if (trxAnno != null) {
                return true;
            }
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //判斷方法上是否有注解@GlobalTransactional
                trxAnno = method.getAnnotation(GlobalTransactional.class);
                if (trxAnno != null) {
                    return true;
                }
                 //判斷方法上是否有注解@GlobalLock
                GlobalLock lockAnno = method.getAnnotation(GlobalLock.class);
                if (lockAnno != null) {
                    return true;
                }
            }
        }
    }
    return false;
}

wrapIfNecessary方法中實(shí)現(xiàn)了事務(wù)注解識(shí)別的核心邏輯:

/**
 * The following will be scanned, and added corresponding interceptor:
 *
 * TM:
 * @see io.seata.spring.annotation.GlobalTransactional // TM annotation
 * Corresponding interceptor:
 * @see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalTransaction(MethodInvocation, AspectTransactional) // TM handler
 *
 * GlobalLock:
 * @see io.seata.spring.annotation.GlobalLock // GlobalLock annotation
 * Corresponding interceptor:
 * @see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalLock(MethodInvocation, GlobalLock)  // GlobalLock handler
 *
 * TCC mode:
 * @see io.seata.rm.tcc.api.LocalTCC // TCC annotation on interface
 * @see io.seata.rm.tcc.api.TwoPhaseBusinessAction // TCC annotation on try method
 * @see io.seata.rm.tcc.remoting.RemotingParser // Remote TCC service parser
 * Corresponding interceptor:
 * @see io.seata.spring.tcc.TccActionInterceptor // the interceptor of TCC mode
 */
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // do checkers
    // seata提供的有擴(kuò)展邏輯用于輔助判斷是否需要增強(qiáng)
    if (!doCheckers(bean, beanName)) {
        return bean;
    }
    try {
        synchronized (PROXYED_SET) {
            //如果已被代理,則跳過該Bean ,PROXYED_SET是一個(gè)Set<String> 集合
            if (PROXYED_SET.contains(beanName)) {
                return bean;
            }
            interceptor = null;
            //check TCC proxy
            //判斷是否TCC模式,如果是TCC,則添加TCC 攔截器
            if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                // init tcc fence clean task if enable useTccFence
                TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                        (ConfigurationChangeListener)interceptor);
            } else {
                //非TCC模式
                // 查詢Bean的 Class 類型
                Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
                //判斷是否有GlobalTransactional或者GlobalLock注解,如果沒有就不會(huì)代理,直接返回bean
                if (!existsAnnotation(new Class[]{serviceInterface})
                    && !existsAnnotation(interfacesIfJdk)) {
                    return bean;
                }
                // 初始化GlobalTransactionalInterceptor
                if (globalTransactionalInterceptor == null) {
                    globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                    //添加 禁用全局事務(wù)的監(jiān)聽器
                    ConfigurationCache.addConfigListener(
                            ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener)globalTransactionalInterceptor);
                }
                interceptor = globalTransactionalInterceptor;
            }
            LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
            // 判斷是否已經(jīng)是AOP代理類,如果不是,則執(zhí)行父類的wrapIfNecessary
            if (!AopUtils.isAopProxy(bean)) {
                bean = super.wrapIfNecessary(bean, beanName, cacheKey);
            } else {
                // 給代理對(duì)象添加攔截器
                AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                int pos;
                for (Advisor avr : advisor) {
                    // Find the position based on the advisor's order, and add to advisors by pos
                    pos = findAddSeataAdvisorPosition(advised, avr);
                    advised.addAdvisor(pos, avr);
                }
            }
            //標(biāo)識(shí)該bean已經(jīng)代理過,本方法入口處有判斷
            PROXYED_SET.add(beanName);
            return bean;
        }
    } catch (Exception exx) {
        throw new RuntimeException(exx);
    }
}

Seata 提供的有擴(kuò)展邏輯用于輔助判斷是否需要增強(qiáng),這里的擴(kuò)展點(diǎn)有好幾個(gè),這些擴(kuò)展是用于安全保障,使用者可以按需采用。

三、小結(jié):

本篇梳理了引入seata-spring-boot-starter模塊后,其內(nèi)部會(huì)通過的自動(dòng)裝配機(jī)制會(huì)在SeataAutoConfiguration類中,掃描具有@GlobalTransactional全局事務(wù)注解的類和方法的 bean,并對(duì)這類 bean 添加GlobalTransactionalInterceptor,進(jìn)行 AOP 增強(qiáng),加入分布式事務(wù)的能力,增強(qiáng)后的功能復(fù)雜,下篇繼續(xù),更多關(guān)于全局事務(wù)注解@GlobalTransactional的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于bigDecimal類的精度保留方法

    關(guān)于bigDecimal類的精度保留方法

    這篇文章主要介紹了關(guān)于bigDecimal類的精度保留方法,計(jì)算機(jī)存儲(chǔ)的浮點(diǎn)數(shù)受存儲(chǔ)bit位數(shù)影響,只能保證一定范圍內(nèi)精準(zhǔn),超過bit范圍的只能取近似值,Java使用java.math.BigDecimal專門處理小數(shù)精度,需要的朋友可以參考下
    2023-07-07
  • springboot3?redis?常用操作工具類詳解

    springboot3?redis?常用操作工具類詳解

    本文詳細(xì)介紹了Spring Boot 3中使用Spring Data Redis進(jìn)行Redis操作的工具類實(shí)現(xiàn),該工具類涵蓋了字符串、哈希、列表、集合和有序集合等常用功能,感興趣的朋友一起看看吧
    2025-01-01
  • SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明

    SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明

    這篇文章主要介紹了SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java面試之動(dòng)態(tài)規(guī)劃與組合數(shù)

    Java面試之動(dòng)態(tài)規(guī)劃與組合數(shù)

    這篇文章主要介紹了Java面試之動(dòng)態(tài)規(guī)劃與組合數(shù)的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Javadoc標(biāo)簽和Javadoc注釋規(guī)范說明

    Javadoc標(biāo)簽和Javadoc注釋規(guī)范說明

    這篇文章主要介紹了Javadoc標(biāo)簽和Javadoc注釋規(guī)范說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • MyBatis控制臺(tái)顯示SQL語(yǔ)句的方法實(shí)現(xiàn)

    MyBatis控制臺(tái)顯示SQL語(yǔ)句的方法實(shí)現(xiàn)

    這篇文章主要介紹了MyBatis控制臺(tái)顯示SQL語(yǔ)句的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java導(dǎo)出Word文檔的四種方法

    Java導(dǎo)出Word文檔的四種方法

    在日常的開發(fā)工作中,我們時(shí)常會(huì)遇到導(dǎo)出Word文檔報(bào)表的需求,比如公司的財(cái)務(wù)報(bào)表、醫(yī)院的患者統(tǒng)計(jì)報(bào)表、電商平臺(tái)的銷售報(bào)表等等,所以本文給大家介紹了Java導(dǎo)出Word文檔的四種方法,并通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • SpringBoot的Bean類三種注入方式詳解

    SpringBoot的Bean類三種注入方式詳解

    在 Spring Boot 中,Bean 的注入方式主要包括構(gòu)造函數(shù)注入(Constructor Injection)、字段注入(Field Injection)以及 Setter 方法注入(Setter Injection),以下是這幾種注入方式的詳細(xì)介紹,并附上 Lombok 的注入方式,需要的朋友可以參考下
    2024-12-12
  • Mybatis中3種關(guān)聯(lián)關(guān)系的實(shí)現(xiàn)方法示例

    Mybatis中3種關(guān)聯(lián)關(guān)系的實(shí)現(xiàn)方法示例

    這篇文章主要給大家介紹了關(guān)于Mybatis中3種關(guān)聯(lián)關(guān)系的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Java Web實(shí)現(xiàn)session過期后自動(dòng)跳轉(zhuǎn)到登陸頁(yè)功能【基于過濾器】

    Java Web實(shí)現(xiàn)session過期后自動(dòng)跳轉(zhuǎn)到登陸頁(yè)功能【基于過濾器】

    這篇文章主要介紹了Java Web實(shí)現(xiàn)session過期后自動(dòng)跳轉(zhuǎn)到登陸頁(yè)功能,涉及java過濾器針對(duì)session的判斷與跳轉(zhuǎn)相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11

最新評(píng)論

民乐县| 吕梁市| 札达县| 乌拉特前旗| 张家口市| 永顺县| 比如县| 大方县| 罗甸县| 绥阳县| 临高县| 姚安县| 余干县| 化德县| 温泉县| 连南| 汉中市| 万全县| 尼玛县| 长丰县| 新竹县| 新宁县| 磐石市| 甘德县| 扶沟县| 密云县| 南溪县| 新河县| 鹤峰县| 溧水县| 天峨县| 建昌县| 安丘市| 平原县| 垦利县| 英吉沙县| 延津县| 石楼县| 龙口市| 舞钢市| 商南县|