1. 程式人生 > >Spring提取@Transactional事務註解的原始碼解析

Spring提取@Transactional事務註解的原始碼解析

宣告:本編文章是自己在檢視Spring提取@Transactional註解的原始碼過程中隨手記下的筆記,只做了大概流程的記錄,未做詳細分析,如有錯誤還請諒解。

這裡寫圖片描述

1、事務切面匹配處理類

AopUtils#canApply(Pointcut, Class , boolean)
方法中會呼叫到 TransactionAttributeSourcePointcut#matches 方法

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            //methodMatcher.matches(method, targetClass) 方法會匹配對應的處理類,在Transaction提取的過程中會匹配到:TransactionAttributeSourcePointcut 
            if ((introductionAwareMethodMatcher != null &&
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

這裡寫圖片描述

2、事務切點匹配

TransactionAttributeSourcePointcut#matches

在閱讀TransactionAttributeSourcePointcut內的原始碼的時候,我們發現該類是一個抽象,但是他確沒有實現的子類!!!那麼這個類到底在哪被引用了呢?

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

    public boolean matches(Method method, Class targetClass) {

        // 該處呼叫了 getTransactionAttributeSource() 的抽象方法,但是卻沒有子類實現這個方法,這是怎麼一回事呢?
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof TransactionAttributeSourcePointcut)) {
            return false;
        }
        TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other;
        return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource());
    }

    @Override
    public int hashCode() {
        return TransactionAttributeSourcePointcut.class.hashCode();
    }

    @Override
    public String toString() {
        return getClass().getName() + ": " + getTransactionAttributeSource();
    }


    /**
     * Obtain the underlying TransactionAttributeSource (may be {@code null}).
     * To be implemented by subclasses.
     */
    protected abstract TransactionAttributeSource getTransactionAttributeSource();

}

3、TransactionAttributeSourcePointcut 抽象類的應用

我們懷著上面的疑問全域性搜尋 TransactionAttributeSourcePointcut 可以在 BeanFactoryTransactionAttributeSourceAdvisor 裡面找到如下的程式碼:

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    private TransactionAttributeSource transactionAttributeSource;

    // 此處利用了匿名內部類的方式例項化了 TransactionAttributeSourcePointcut 物件,在此我們找到了上面問題的答案。
    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
        @Override
        protected TransactionAttributeSource getTransactionAttributeSource() {
            return transactionAttributeSource;
        }
    };


    /**
     * Set the transaction attribute source which is used to find transaction
     * attributes. This should usually be identical to the source reference
     * set on the transaction interceptor itself.
     * @see TransactionInterceptor#setTransactionAttributeSource
     */
    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionAttributeSource = transactionAttributeSource;
    }

    /**
     * Set the {@link ClassFilter} to use for this pointcut.
     * Default is {@link ClassFilter#TRUE}.
     */
    public void setClassFilter(ClassFilter classFilter) {
        this.pointcut.setClassFilter(classFilter);
    }

    public Pointcut getPointcut() {
        return this.pointcut;
    }

}

3、TransactionAttributeSource 屬性的 Bean 定義過程

其實,在例項化 BeanFactoryTransactionAttributeSourceAdvisor 時,Spring 已經為我們的 BeanFactoryTransactionAttributeSourceAdvisor 設定了 TransactionAttributeSource 屬性,可以進入 AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer#configureAutoProxyCreator 方法中看原始碼:

private static class AopAutoProxyConfigurer {

    public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

        String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
        if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
            Object eleSource = parserContext.extractSource(element);

            // 註解事務 transactionAttributeSource Spring 定義的Bean為: AnnotationTransactionAttributeSource 例項
            // Create the TransactionAttributeSource definition.
            RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
            sourceDef.setSource(eleSource);
            sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

            // Create the TransactionInterceptor definition.
            RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
            interceptorDef.setSource(eleSource);
            interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registerTransactionManager(element, interceptorDef);
            interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

            // create BeanFactoryTransactionAttributeSourceAdvisor Bean 的定義
            // Create the TransactionAttributeSourceAdvisor definition.
            RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
            advisorDef.setSource(eleSource);
            advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 設定 transactionAttributeSource 屬性
            advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
            if (element.hasAttribute("order")) {
                advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
            }
            parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

            CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
            compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
            parserContext.registerComponent(compositeDef);
        }
    }
}

4、TransactionAttributeSource#getTransactionAttribute 方法的呼叫過程

通過以上的分析,我們可以確定
TransactionAttributeSourcePointcut#getTransactionAttributeSource 返回的是:AnnotationTransactionAttributeSource 例項,AnnotationTransactionAttributeSource繼承自:AbstractFallbackTransactionAttributeSource, 故此TransactionAttributeSourcePointcut#matches 最終會呼叫到 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

    public boolean matches(Method method, Class targetClass) {
        TransactionAttributeSource tas = getTransactionAttributeSource();

        // 最終會呼叫到 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

    // 省略其他程式碼 ……………………
}

再看 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法

// 獲取事務屬性
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
    // First, see if we have a cached value.
    Object cacheKey = getCacheKey(method, targetClass);
    Object cached = this.attributeCache.get(cacheKey);
    if (cached != null) {
        // Value will either be canonical value indicating there is no transaction attribute,
        // or an actual transaction attribute.
        if (cached == NULL_TRANSACTION_ATTRIBUTE) {
            return null;
        }
        else {
            return (TransactionAttribute) cached;
        }
    }
    else {
        // We need to work it out.  根據 method、targetClass 推算事務屬性,TransactionAttribute 
        TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
        // Put it in the cache.
        if (txAtt == null) {
            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Adding transactional method '" + method.getName() + "' with attribute: " + txAtt);
            }
            this.attributeCache.put(cacheKey, txAtt);
        }
        return txAtt;
    }
}

5、事務屬性的推算過程:

// 推算事務屬性,TransactionAttribute 
private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    // Ignore CGLIB subclasses - introspect the actual user class.
    Class<?> userClass = ClassUtils.getUserClass(targetClass);
    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
    // If we are dealing with method with generic parameters, find the original method.
    specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

    // 通過上面的分析,findTransactionAttribute 該方法最終會呼叫到:AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.Class<?>) 

    // First try is the method in the target class. 方式1: 從目標類的方法上找 Transaction註解
    TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
    if (txAtt != null) {
        return txAtt;
    }

    // Second try is the transaction attribute on the target class.  方式2: 從目標類上找 Transaction註解
    txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAtt != null) {
        return txAtt;
    }

    if (specificMethod != method) {// 以上兩種方式如果還沒有找到 TransactionAttribute 屬性,那就要從目標類的介面開始找
        // Fallback is to look at the original method.  方式3:介面的方法上找 Transaction註解
        txAtt = findTransactionAttribute(method);
        if (txAtt != null) {
            return txAtt;
        }
        // Last fallback is the class of the original method.  方式4:介面的類上找 Transaction註解
        return findTransactionAttribute(method.getDeclaringClass());
    }
    return null;
}

6、事務註解屬性的解析

AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.Class

7、獲取事務註解

public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {

    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
        //獲取 Transactional 註解
        Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
        if (ann != null) {
            //從 @Transactional 註解上獲取事務屬性值,幷包裝成 TransactionAttribute 返回
            return parseTransactionAnnotation(ann);
        }
        else {
            return null;
        }
    }

    public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
        rbta.setPropagationBehavior(ann.propagation().value());
        rbta.setIsolationLevel(ann.isolation().value());
        rbta.setTimeout(ann.timeout());
        rbta.setReadOnly(ann.readOnly());
        rbta.setQualifier(ann.value());
        ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
        Class[] rbf = ann.rollbackFor();
        for (Class rbRule : rbf) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] rbfc = ann.rollbackForClassName();
        for (String rbRule : rbfc) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        Class[] nrbf = ann.noRollbackFor();
        for (Class rbRule : nrbf) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] nrbfc = ann.noRollbackForClassName();
        for (String rbRule : nrbfc) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        rbta.getRollbackRules().addAll(rollBackRules);
        return rbta;
    }

    @Override
    public boolean equals(Object other) {
        return (this == other || other instanceof SpringTransactionAnnotationParser);
    }

    @Override
    public int hashCode() {
        return SpringTransactionAnnotationParser.class.hashCode();
    }

}

相關推薦

Spring提取@Transactional事務註解原始碼解析

宣告:本編文章是自己在檢視Spring提取@Transactional註解的原始碼過程中隨手記下的筆記,只做了大概流程的記錄,未做詳細分析,如有錯誤還請諒解。 1、事務切面匹配處理類 AopUtils#canApply(Pointcut, Class ,

Spring宣告式事務管理原始碼解析

核心介面 1. PlatformTransactionManager:事務管理器頂級介面:各持久化框架要想接入Spring的事務管理,必須自行提供該介面實現 2. TransactionDefinition:事務的屬性頂級介面:其實現類封裝事務隔離級別、

非同步任務spring @Async註解原始碼解析

1.引子 開啟非同步任務使用方法: 1).方法上加@Async註解  2).啟動類或者配置類上@EnableAsync 2.原始碼解析 雖然spring5已經出來了,但是我們還是使用的spring4,本文就根據spring-context-4.3.14.RELEASE.jar來分析原始碼。 2.1

Spring boot非同步註解原始碼解析

一、例子 我們先來看下面這個Demo。 pom.xml中maven依賴: <parent> <gro

spring @Transactional 事務註解的坑

分布 記錄 view 文件中 指定 span 團隊 獲取數據 prot 1. 在需要事務管理的地方加@Transactional 註解。@Transactional 註解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。 2. @Transactiona

Spring Boot @Enable*註解原始碼解析及自定義@Enable*

  Spring Boot 一個重要的特點就是自動配置,約定大於配置,幾乎所有元件使用其本身約定好的預設配置就可以使用,大大減輕配置的麻煩。其實現自動配置一個方式就是使用@Enable*註解,見其名知其意也,即“使什麼可用或開啟什麼的支援”。 ### Spring Boot 常用@Enable* 首先來簡

Spring 中 bean 註冊的原始碼解析

前言 所謂 bean 的註冊,就是把在配置檔案中配置的 <bean> 節點載入配記憶體中,供後續使用。 bean的註冊主要分為兩個階段,一個是準備階段,就是對配置檔案進行解析,把配置檔案載入到記憶體中,以 Document 的形式存放;第二個階段是對 Document 進行操作,獲取其中的節

Spring載入bean定義流程原始碼解析

在本文中,先對Spring載入和建立bean例項的流程跟著原始碼走一遍,在後續的文章中再對所涉及的類的其他方法具體介紹。 //這一步是載入指定的配置檔案 Resource resource = new ClassPathResource("bean.xm

Spring IOC容器啟動流程原始碼解析(一)——容器概念詳解及原始碼初探

目錄 1. 前言 1.1 IOC容器到底是什麼 IOC和AOP是Spring框架的核心功能,而IOC又是AOP實現的基礎,因而可以說IOC是整個Spring框架的基石。那麼什麼是IOC?IOC即控制反轉,通俗的說就是讓Spring框架來幫助我們完成物件的依賴管理和生命週期控制等等工作。從面向物件的角度來說,

Spring IOC容器啟動流程原始碼解析(四)——初始化單例項bean階段

目錄 1. 引言 2. 初始化bean的入口 3 嘗試從當前容器及其父容器的快取中獲取bean 3.1 獲取真正的beanName 3.2 嘗試從當前容器的快取中獲取bean 3.3 從父容器中查詢bean 3.4 解析bean的依賴 3.5 再一

Spring cloud oauth2.0的原始碼解析與實踐Demo

一、原始碼程式碼介紹 上一篇已經大致介紹了一下Spring cloud oauth2.0,點此檢視上一篇,現在再來大致分析一下它的原始碼結構,Spring cloud oauth2.0的程式碼結構圖如下: 可以看到Spring oauth2.0的程式碼結構分為了五層,c

楊老師課堂之springAOP事務控制原始碼解析

spring AOP基於動態代理實現,想看懂原始碼必須瞭解動態代理和位元組碼增強方面的知識。 基於對spring各種配置的瞭解,首先我們先從DataSourse由誰來管理入手。瞭解AOP。 一般來講首先會配置一個datasource,至於你配置什麼連線池還是用JNDI這

Spring中@Transactional事務回滾

一、Spring 預設事務 Spring中@Transactional事務,預設情況下只對 RuntimeException 回滾。 即: 如果被註解的資料庫操作方法中發生了unchecked異常(RuntimeException),所有的資料庫操作將roll

spring之@Transactional事務傳播性

Required:必須有邏輯事務,否則新建一個事務,使用PROPAGATION_REQUIRED指定,表示如果當前存在一個邏輯事務,則加入該邏輯事務,否則將新建一個邏輯事務,如圖9-2和9-3所示;   圖9-2 Required傳播行為   圖9-3 Require

被忽略的Spring3小改進—支援多資料來源的@Transactional事務註解

問題描述 有個專案在配置Spring事務時,使用了@Transactional註解,但這個專案使用了多個數據源,而事務註解只對第一個起作用,咋辦? 探幽 先回顧一下之前個人習慣使用的 + 配置方式,大致步驟如下: 1)定義與事務管理器對應的事

必須加@Transactional事務註解才能成功插入資料,原因未知。

專案名:patentFlowManagementProject 如果不加事務註解,資料插入失敗而且無任何報錯,很詭異。 @Override @Transactional public void joinPrefecture(Strin

Mybaits 原始碼解析 (十)----- 全網最詳細,沒有之一:Spring-Mybatis框架使用與原始碼解析

在前面幾篇文章中我們主要分析了Mybatis的單獨使用,在實際在常規專案開發中,大部分都會使用mybatis與Spring結合起來使用,畢竟現在不用Spring開發的專案實在太少了。本篇文章便來介紹下Mybatis如何與Spring結合起來使用,並介紹下其原始碼是如何實現的。 Spring-Mybatis使用

Spring Security登入驗證流程原始碼解析

一、登入認證基於過濾器鏈 Spring Security的登入驗證流程核心就是過濾器鏈。當一個請求到達時按照過濾器鏈的順序依次進行處理,通過所有過濾器鏈的驗證,就可以訪問API介面了。 SpringSecurity提供了多種登入認證的方式,由多種Filter過濾器來實現,比如: BasicAuthent

Spring Transaction 5.1.3 原始碼分析 : AnnotationTransactionAttributeSource 解析註解事務屬性

概述 Spring支援使用註解指定服務方法的事務屬性。這些事務註解可以用在類上,也可以用在方法上,如下例子所示 。 Spring事務註解例子–應用在類級別 將事務註解標記到服務元件類級別,相當於為該服務元件的每個服務方法都應用了這個註解。 import org.sprin

Java事務處理全解析(七)—— 像Spring一樣使用Transactional註解(Annotation)

在本系列的上一篇文章中,我們講到了使用動態代理的方式完成事務處理,這種方式將service層的所有public方法都加入到事務中,這顯然不是我們需要的,需要代理的只是那些需要操作資料庫的方法。在本篇中,我們將講到如何使用Java註解(Annotation)來標記需要事務處