Spring Boot Transactional註解原始碼閱讀筆記(二)

Lorenzo君發表於2018-08-05

  在原始碼筆記(一)中,我們留下了幾個問題:

  • Spring Boot是怎麼掃描到我們的bean裡面有 Transactional 這個註解,並且把 InfrastructureAdvisorAutoProxyCreator 這個 BeanPostProcessor註冊到bean的資訊裡面去的。
  • Spring Boot生成的cglib proxy在呼叫帶有 Transactional 註解的方法前到底做了什麼,它插入了哪些程式碼,這些程式碼是什麼含義。

  今天這篇文章將要談一談第一個問題:Spring Boot是怎麼掃描到我們的bean裡面有 Transactional 這個註解,並且把 InfrastructureAdvisorAutoProxyCreator 這個 BeanPostProcessor註冊到bean的資訊裡面去的

1. 定位程式碼位置

  在筆記一里面我們提到,在生成cglib proxy的過程中,會在 AbstractAutowireCapableBeanFactory裡面呼叫 getBeanPostProcessors方法,這個方法返回的是一個叫beanPostProcessors的成員變數,通過搜尋我們發現,AbstractAutowireCapableBeanFactory 的父類 AbstractBeanFactory有一個addBeanPostProcessor的方法,這個方法會把我們需要跟蹤的InfrastructureAdvisorAutoProxyCreator加到beanPostProcessors這個list中。通過斷點,我們可以看到現在已經找到了InfrastructureAdvisorAutoProxyCreator被加到list中,左邊紅色的方框是呼叫棧,可以看到程式碼呼叫的順序。

Spring Boot Transactional註解原始碼閱讀筆記(二)
  通過上面的呼叫棧,可以找到PostProcessorRegistrationDelegate裡面有一個這樣的方法:registerBeanPostProcessors(ConfigurableListableBeanFactory,AbstractApplicationContext),方法裡面有這樣一段程式碼:

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
複製程式碼

  通過debug看到這個postProcessorNames變數的值如下圖,紅色框出來的是InfrastructureAdvisorAutoProxyCreator這個bean的名稱。 閱讀程式碼我們知道postProcessorNames來源於getBeanNamesForType,我們有必要跟蹤進去,看看這個getBeanNamesForType做了什麼。

Spring Boot Transactional註解原始碼閱讀筆記(二)
  進入到getBeanNamesForType方法之後,我們很快就發現在DefaultListableBeanFactory裡面有一個beanDefinitionNames的變數,從它的值裡面我們可以找到internalAutoProxyCreator,我們需要知道這個beanName是什麼時候被加到beanDefinitionNames裡面去的。在DefaultListableBeanFactory裡面有一個registerBeanDefinition的方法,我們打上斷點,加上條件命中,進行觀察。左側是呼叫棧,有了這個我們就能很方便的知道這個internalAutoProxyCreator是怎麼來的。

Spring Boot Transactional註解原始碼閱讀筆記(二)
  跟著呼叫棧看了一會,發現這些地方只是把解析Transactional註解的類註冊到了DefaultListableBeanFactory裡面,並沒有涉及到如何的解析註解,似乎方向錯了。

2. 轉機

  回到InfrastructureAdvisorAutoProxyCreator,我們看看是不是它在處理bean的時候解析了Transactional註解。它的父類AbstractAutoProxyCreator的方法wrapIfNecessary中有以下一段程式碼:

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
複製程式碼

  跳轉到AbstractAdvisorAutoProxyCreator的方法getAdvicesAndAdvisorsForBean,其中有這樣一段程式碼:

List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
複製程式碼

  跳轉到findEligibleAdvisors,其中有一段程式碼:

List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
複製程式碼

  進到findAdvisorsThatCanApply方法,看到以下程式碼:

return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
複製程式碼

  跟著程式執行的順序,最後走到了AopUtils的方法canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions),方法中有一段程式碼

if ((introductionAwareMethodMatcher != null &&
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
						methodMatcher.matches(method, targetClass)) {
					return true;
}
複製程式碼

進到methodMatcher.matches,看到下面這段程式碼:

return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
複製程式碼

  接著進入tas.getTransactionAttribute最終會來到類AbstractFallbackTransactionAttributeSource,在它的getTransactionAttribute方法中,我們重點看一下下面這行程式碼:

TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
複製程式碼

  進到方法的裡面,執行到下面程式碼:

TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
複製程式碼

  findTransactionAttribute 方法由AbstractFallbackTransactionAttributeSource的子類AnnotationTransactionAttributeSource 實現,繼續跟蹤到了determineTransactionAttribute方法,可以看到這個方法的程式碼是這樣的:

if (ae.getAnnotations().length > 0) {
		for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
			TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
			if (attr != null) {
				return attr;
			}
		}
	}
return null;
複製程式碼

  終於開始判斷方法上面註解的個數,這可能意味著我們快要找到spring是在哪兒解析Transactional註解了。我們接著往下執行,進到了SpringTransactionAnnotationParser類中,它的方法parseTransactionAnnotation有這麼一段程式碼:

AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
複製程式碼

  這一段就是用來解析我們transactional註解的,至此我們已經找到了spring是在什麼地方解析bean裡面的transactional註解的。

3.總結

  本期解決了筆記(一)留下的兩個問題中一個,接下來會花一些時間來跟蹤一下cglib proxy對帶有Transactional註解的方法做了什麼。

插播一段廣告,阿里巴巴長期招聘,有需要內推的朋友可以加脈脈私聊,或者簡歷發到linlan.zcj@alibaba-inc.com

Spring Boot Transactional註解原始碼閱讀筆記(二)

相關文章