Spring 源碼(七)Spring 事務(wù)源碼解析

注冊后置處理器開啟對事務(wù)的支持

@EnableTransactionManagement

@EnableTransactionManagement注解的主要作用是開啟對事務(wù)的支持啼辣,源碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default Ordered.LOWEST_PRECEDENCE;

}

這里最核心的是TransactionManagementConfigurationSelector類,這個類主要的作用是通過ImportSelector注冊了AutoProxyRegistrarProxyTransactionManagementConfiguration2個組件御滩,源碼如下:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                // 注冊 InfrastructureAdvisorAutoProxyCreator 后置處理器和事務(wù)管理器組件
                return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}

AutoProxyRegistrar

AutoProxyRegistrar 的主要作用是將InfrastructureAdvisorAutoProxyCreator后置處理器注冊到容器鸥拧,注冊這個后置處理器和上一篇Spring AOP注冊AnnotationAwareAspectJAutoProxyCreator后置處理器一樣,這里就不在重復(fù)說明了削解。InfrastructureAdvisorAutoProxyCreator 他是實現(xiàn)了BeanPostProcessor 接口的后置處理器富弦,所以所有 Bean 的初始化都會調(diào)用其 postProcessAfterInitialization 方法,這個方法的實現(xiàn)是在其父類AbstractAutoProxyCreator類中氛驮。

ProxyTransactionManagementConfiguration

我們通過ProxyTransactionManagementConfiguration來注冊事務(wù)管理器組件舆声,這個類本身也是一個配置類。在這個配置類中我們將會注冊一下三個組件:

  • BeanFactoryTransactionAttributeSourceAdvisor:事務(wù)增強(qiáng)器柳爽,包含了切面組件 TransactionInterceptor和標(biāo)簽解析器TransactionAttributeSource
  • TransactionAttributeSource:@Transaction注解標(biāo)簽解析器
  • TransactionInterceptor:保存了事務(wù)屬性信息媳握,事務(wù)管理器;它本身也是一個方法攔截器磷脯,在invoke方法中進(jìn)行了事務(wù)的處理蛾找。

創(chuàng)建代理Bean

上面我們說了所以所有 Bean 的初始化都會調(diào)用其 AbstractAutoProxyCreator#postProcessAfterInitialization 方法來完成Bean的增強(qiáng),我們跟進(jìn)去可以看到這段代碼:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

可以看到生代理對象是在wrapIfNecessary(bean, beanName, cacheKey);方法中完成的赵誓,源碼如下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    
    // 如果存在建言那么久創(chuàng)建代理類
    // 獲取攔截鏈
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 使用攔截鏈創(chuàng)建代理對象打毛,對原有的Bean進(jìn)行增強(qiáng)
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

找到攔截鏈的的核心方法是 BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans方法

findAdvisorBeans:67, BeanFactoryAdvisorRetrievalHelper (org.springframework.aop.framework.autoproxy)
findCandidateAdvisors:102, AbstractAdvisorAutoProxyCreator (org.springframework.aop.framework.autoproxy)
findEligibleAdvisors:88, AbstractAdvisorAutoProxyCreator (org.springframework.aop.framework.autoproxy)
getAdvicesAndAdvisorsForBean:70, AbstractAdvisorAutoProxyCreator (org.springframework.aop.framework.autoproxy)
wrapIfNecessary:346, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
postProcessAfterInitialization:298, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
applyBeanPostProcessorsAfterInitialization:423, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:1638, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:555, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:483, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:312, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:308, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:761, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support)
refresh:543, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼如下:

public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = null;
    synchronized (this) {
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // 獲取所有增強(qiáng)器的名稱
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }

    List<Advisor> advisors = new LinkedList<Advisor>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    // 根據(jù)名稱增強(qiáng)器
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                ...
            }
        }
    }
    // 返回攔截鏈
    return advisors;
}

創(chuàng)建代理Bean的核心流程:

  1. 單例Bean初始化完成后執(zhí)行后置處理器 AbstractAutoProxyCreator#postProcessAfterInitialization 方法
  2. 在容器中找Advisor類型的所有增強(qiáng)器名稱柿赊,這就會將與事務(wù)相關(guān)的增強(qiáng)器BeanFactoryTransactionAttributeSourceAdvisor找出來
  3. 根據(jù)增強(qiáng)器名稱獲取對應(yīng)的實例,并生成攔截鏈
  4. 判斷代理類型
  5. 根據(jù)不同的代理類型和攔截鏈創(chuàng)建代理對象

執(zhí)行業(yè)務(wù)方法進(jìn)行攔截

前面AOP說過不管理是JdkDynamicAopProxy還是CglibAopProxy代理幻枉,他們的執(zhí)行最終都會去調(diào)用MethodInterceptor.invoke()方法碰声,而我們事務(wù)對應(yīng)的方法攔截器是TransactionInterceptor類。也就是說我們對事務(wù)的增強(qiáng)起始是在TransactionInterceptorinvoke方法中熬甫。源碼如下:

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
    ...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
        @Override
        public Object proceedWithInvocation() throws Throwable {
            return invocation.proceed();
        }
    });
}

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {

    // 獲取事務(wù)屬性
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    // 獲取事務(wù)管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 構(gòu)造方法唯一標(biāo)示
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 聲明式事務(wù)
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 創(chuàng)建事務(wù)
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // 執(zhí)行被增強(qiáng)的方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // 異骋忍簦回滾
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 清除信息
            cleanupTransactionInfo(txInfo);
        }
        // 提交事務(wù)
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    // 編程式事務(wù)
    ...
}

從上面我們的源碼可以看出,一個事務(wù)處理的標(biāo)準(zhǔn)流程:

  1. createTransactionIfNecessary 創(chuàng)建一個事務(wù)
  2. invocation.proceedWithInvocation(); 執(zhí)行業(yè)務(wù)方法
  3. completeTransactionAfterThrowing(txInfo, ex); 如果遇到異常椿肩,事務(wù)回滾
  4. commitTransactionAfterReturning(txInfo); 如果沒有異常就提交事務(wù)

在創(chuàng)建瞻颂,回滾和提交事務(wù)方法中還有的很多對嵌套事務(wù)的邏輯,比如事務(wù)的傳遞性郑象,事務(wù)回滾的條件判斷等贡这,這里就不說了,有興趣自己去跟下源碼厂榛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盖矫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子击奶,更是在濱河造成了極大的恐慌辈双,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件正歼,死亡現(xiàn)場離奇詭異辐马,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)局义,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門喜爷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萄唇,你說我怎么就攤上這事檩帐。” “怎么了另萤?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵湃密,是天一觀的道長。 經(jīng)常有香客問我四敞,道長泛源,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任忿危,我火速辦了婚禮达箍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铺厨。我一直安慰自己缎玫,他們只是感情好硬纤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赃磨,像睡著了一般筝家。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邻辉,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天溪王,我揣著相機(jī)與錄音,去河邊找鬼恩沛。 笑死在扰,一個胖子當(dāng)著我的面吹牛缕减,可吹牛的內(nèi)容都是我干的雷客。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼桥狡,長吁一口氣:“原來是場噩夢啊……” “哼搅裙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起裹芝,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤部逮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嫂易,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兄朋,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年怜械,在試婚紗的時候發(fā)現(xiàn)自己被綠了颅和。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡缕允,死狀恐怖峡扩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情障本,我是刑警寧澤教届,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站驾霜,受9級特大地震影響案训,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粪糙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一强霎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猜旬,春花似錦脆栋、人聲如沸倦卖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怕膛。三九已至,卻和暖如春秦踪,著一層夾襖步出監(jiān)牢的瞬間褐捻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工椅邓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柠逞,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓景馁,卻偏偏與公主長得像板壮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子合住,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容