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)來標記需要事務處