基于 @Transactional 的聲明式事務(wù)原理剖析
面試中經(jīng)常會被問到:“為什么 Spring 通過一個注解就可以實(shí)現(xiàn)事務(wù)管理呢?”,一般大家聽到這個問題都會回答:因?yàn)?Spring 是通過 AOP 實(shí)現(xiàn)的。當(dāng)再被追問道“那你能詳細(xì)說一下 @Transactional 的工作機(jī)制嗎?它是如何通過 AOP 控制事務(wù)的?”的時候,很多小伙伴就開始支支吾吾,亂說一通了 ??。
在上一篇文章Spring 是如何管理事務(wù)的當(dāng)中,我們討論了有關(guān)于事務(wù)的一些基本概念以及 Spring 管理事務(wù)的兩種方式——編程式和聲明式,并對 @Transactional 注解的源碼及簡單使用進(jìn)行了介紹。今天,我們書接上回,在源碼的層面上對 @Transactional 注解的工作原理進(jìn)行分析,希望通過下面的分析,讓更多的小伙伴了解到 Spring 聲明式事務(wù)的工作原理,讓大家在面試時更從容的回答上述面試問題。
Tip:閱讀本篇內(nèi)容可能需要小伙伴們對 Spring Bean 的生命周期以及 Spring AOP 有些基本的了解。
好的,開始之前我們先明確一下 Spring 使用 @Transactional 注解管理事務(wù)兩個重要內(nèi)容,后邊我們根據(jù)這兩點(diǎn)來逐步揭開它背后的神秘:
- @Transactional 注解的解析時機(jī)?既然大家都知道原理是通過 AOP 實(shí)現(xiàn)的,那么 Spring 何時解析的該注解并生成的代理對象呢?
- 當(dāng)我們業(yè)務(wù)代碼執(zhí)行到事務(wù)方法時,代理對象是如何介入并將事務(wù)管理起來的?
代理生成
在 Spring Bean 的生命周期中,創(chuàng)建 Spring Bean 時有幾個重點(diǎn)方法:
- createBeanInstance():構(gòu)造實(shí)例化對象
- populateBean():屬性裝配
- initializeBean():Bean 的初始化
當(dāng)一個 Spring Bean 被創(chuàng)建時,它會依次執(zhí)行這些方法。在這個階段,Spring 提供了非常多的擴(kuò)展點(diǎn)來插手我們的 Bean 創(chuàng)建過程,而 Spring AOP 就是在執(zhí)行到 initializeBean() 方法時,通過 BPP(BeanPostProcessor)Bean 后置處理器來實(shí)現(xiàn)的。
下面我們通過源碼來逐步分析下(源碼出自 spring-framework-5.3.33),源碼中我們只分析與本文相關(guān)的核心邏輯:
在 initializeBean() 方法中,執(zhí)行了 Bean 的初始化前處理和后處理,其中 AOP 是在后處理中完成的。
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
// 執(zhí)行部分Aware方法
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
} else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 初始化前處理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 執(zhí)行 Bean 的初始化
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
/**
* 執(zhí)行 Bean 的初始化后處理
* AOP 配置方式 DefaultAdvisorAutoProxyCreator 在這里開始介入
*/
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
applyBeanPostProcessorsAfterInitialization() 方法中對 Bean 進(jìn)行了后置處理,處理 AOP 的實(shí)現(xiàn)類為 DefaultAdvisorAutoProxyCreator,BPP 接口的方法實(shí)現(xiàn)最終會找到其父類 AbstractAutoProxyCreator。
AbstractAutoProxyCreator 中對接口方法的實(shí)現(xiàn)如下:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 重點(diǎn)關(guān)注 wrapIfNecessary 方法
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary() 這個方法的作用就是判斷下當(dāng)前的 Bean 是否需要生成代理,需要的話,生成一個代理對象返回。那么,我們被 @Transactional 注解標(biāo)記的類肯定是要生成代理才能執(zhí)行事務(wù)邏輯的,我們繼續(xù)往下分析,看看何時解析的 @Transactional 注解。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey){
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 關(guān)鍵方法,這里查找是否有匹配的 Advisor,有就創(chuàng)建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 創(chuàng)建代理對象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
// 標(biāo)記該類不需要加強(qiáng),并返回普通的 bean 實(shí)例
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
上述邏輯在查找 Advisor,什么是 Advisor?
Advisor 接口是 Spring AOP 中的一個頂層抽象,用于將切點(diǎn)(Pointcut)和通知(Advice)組合關(guān)聯(lián)起來。Advisor 接口的設(shè)計(jì)目的并不是為了提供給 Spring 用戶使用的,而是為了支持不同類型的 Advice,它作為一種內(nèi)部機(jī)制來確保不同類型的 Advice 能夠有一致的支持方式。
實(shí)踐中,我們更關(guān)注它的子接口 PointcutAdvisor。PointcutAdvisor 繼承自 Advisor 接口并增加了 getPointcut() 方法,PointcutAdvisor 通過其持有的 Pointcut 來決定是否對某個連接點(diǎn)(方法調(diào)用)應(yīng)用其 Advice。所以說,這個接口它確保了切點(diǎn)和通知之間的正確關(guān)聯(lián),它是 Advice + Pointcut 組合更好的詮釋。
getAdvicesAndAdvisorsForBean() 方法是一個模板方法,由其子類 AbstractAdvisorAutoProxyCreator 實(shí)現(xiàn),并調(diào)用了本類的 findEligibleAdvisors() 方法:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
/**
* 此處方法調(diào)用不再深入,主要目的是獲取當(dāng)前容器所有的 advisors。
* 通過 getBean() 方法,獲取容器中所有 Advisor 接口類型的 Bean 的集合
*/
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 從獲取到的 Advisor 集合中獲取當(dāng)前 Bean 對象適用的 Advisors
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 對advisor進(jìn)行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findEligibleAdvisors() 方法通過 getBean() 獲取了容器中所有 Advisor 類型的 Bean,然后內(nèi)部調(diào)用 findAdvisorsThatCanApply() 方法從已獲取到的 Advisor 集合中找到可以適配當(dāng)前 Bean 對象的那些 Advisor,該方法內(nèi)部最終是通過層層調(diào)用 AopUtils 的重載方法 canApply() 實(shí)現(xiàn)的:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
} else if (advisor instanceof PointcutAdvisor) {
// 處理事務(wù)的 Advisor 為 BeanFactoryTransactionAttributeSourceAdvisor
// 該類的父類實(shí)現(xiàn)了 PointcutAdvisor 接口
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
} else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
執(zhí)行到此處后,我們找到了處理事務(wù)的 Advisor 為 BeanFactoryTransactionAttributeSourceAdvisor,然后再次執(zhí)行重載的 canApply() 方法,來判斷給定的切點(diǎn)(pca.getPointcut())是否適用于給定的類(targetClass),這里通過 pca.getPointcut() 獲取到的切點(diǎn)為 TransactionAttributeSourcePointcut:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 首先通過類過濾器進(jìn)行匹配,檢查目標(biāo)類是否符合要求
if (!pc.getClassFilter().matches(targetClass)) {
// 不符合,表示該 Pointcut 不能應(yīng)用于此目標(biāo)類
return false;
}
// 這里獲取切點(diǎn)上的方法級別的匹配器
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
// 創(chuàng)建一個 Set 集合來保存目標(biāo)類和其所有接口
Set<Class<?>> classes = new LinkedHashSet<>();
// 如果目標(biāo)類不是代理類,添加到集合
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
// 添加目標(biāo)類的所有接口
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
// 遍歷目標(biāo)類和它的所有接口
for (Class<?> clazz : classes) {
// 通過反射獲取當(dāng)前類聲明的所有方法(包括從父類繼承的方法)
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
// 使用方法匹配器進(jìn)行逐個匹配
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
TransactionAttributeSourcePointcut 這個切點(diǎn)類實(shí)現(xiàn)了 MethodMatcher 接口并重寫了 matches 方法,所以執(zhí)行方法匹配時,會走到如下邏輯:
public boolean matches(Method method, Class<?> targetClass) {
/**
* TransactionAttributeSource 是一個獲取事務(wù)屬性的策略接口(事務(wù)屬性為注解中的屬性配置)
* 這里獲取到的接口實(shí)現(xiàn)為 AnnotationTransactionAttributeSource
* 該類用于解析 @Transactional 注解,并根據(jù)注解中的配置(例如傳播行為、隔離級別等)
* 構(gòu)建相應(yīng)的 TransactionAttribute 對象
*/
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
調(diào)用 AnnotationTransactionAttributeSource 的 getTransactionAttribute() 方法來解析事務(wù)屬性,此方法由其父類 AbstractFallbackTransactionAttributeSource 實(shí)現(xiàn),該方法邏輯比較簡單,內(nèi)部主要是通過調(diào)用本類的 computeTransactionAttribute() 方法,我們直接來看下后邊這個方法:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 非 public 修飾的直接返回 null,返回 null 意味著方法匹配器沒有匹配成功,也就是該切面不會應(yīng)用到這個 Bean,也就不會生成代理
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 這里的主要目的是找到目標(biāo)類標(biāo)記事務(wù)的具體實(shí)現(xiàn)方法,確保事務(wù)配置被正確解析
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 首先嘗試從方法上查找事務(wù)屬性
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// 如果沒有在方法上查找到事務(wù)屬性,嘗試從聲明該方法的類中查找。
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
/**
* 如果找到的具體實(shí)現(xiàn)方法與原始方法對象不同,說明已經(jīng)嘗試過更具體的版本,但未能找到事務(wù)屬性。
* 因此,這里會再次嘗試使用原始方法及其聲明類來查找事務(wù)屬性。
*/
if (specificMethod != method) {
// 使用原始方法查找
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// 使用原始方法的聲明類查找
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
// 如果經(jīng)過上述所有步驟后仍未找到事務(wù)屬性,則返回null,表示該方法不支持事務(wù)管理
return null;
}
通過理解這段代碼的工作機(jī)制,可以看出:
- 非 public 方法是不會生成代理的,所以這里解釋了為什么 @Transactional 注解加在非public 方法事務(wù)會失效的原因;
- 同時,從查找事務(wù)屬性的執(zhí)行邏輯來看,這段代碼也證明了 @Transactional 注解方法級別的配置優(yōu)先于類級別的配置。
上述邏輯中的 findTransactionAttribute() 方法也是一個模板方法,這里會調(diào)用至 AnnotationTransactionAttributeSource 類中,這個方法有兩個重載版本,分別用來處理方法和類級別的注解屬性解析,它們方法內(nèi)部都調(diào)用了同一個方法來進(jìn)行屬性解析:
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
return determineTransactionAttribute(clazz);
}
protected TransactionAttribute findTransactionAttribute(Method method) {
return determineTransactionAttribute(method);
}
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for (TransactionAnnotationParser parser : this.annotationParsers) {
// 遍歷解析器進(jìn)行注解解析,有一個解析器解析成功便直接返回
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
determineTransactionAttribute() 方法中開始遍歷解析器對事務(wù)注解進(jìn)行解析,解析器有 3 種:
- Ejb3TransactionAnnotationParser:用于解析 EJB 3 標(biāo)準(zhǔn)中的 javax.ejb.TransactionAttribute 注解。
- JtaTransactionAnnotationParser:用于解析 JTA 1.2 規(guī)范下的 javax.transaction.Transactional 注解。
- SpringTransactionAnnotationParser:用于解析 Spring 框架的 @Transactional 注解。
這里解析 @Transactional 注解的解析器為 SpringTransactionAnnotationParser。我們繼續(xù)看下 SpringTransactionAnnotationParser 中 parseTransactionAnnotation() 方法的解析邏輯:
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
// 從目標(biāo)元素上獲取指定類型的注解屬性,這里注解是 Transactional
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
// 獲取到調(diào)用重載方法執(zhí)行解析
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
parseTransactionAnnotation() 方法中又調(diào)用了其重載方法,執(zhí)行具體的屬性解析邏輯,下面我們看下這段解析代碼的邏輯,相信大家看到這里應(yīng)該會非常熟悉,這里解析的就是我們在 @Transactional 注解中配置的屬性:
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation"); // 事務(wù)傳播方式
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation"); // 事務(wù)隔離級別
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue()); // 事務(wù)超時時間
String timeoutString = attributes.getString("timeoutString");
Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
"Specify 'timeout' or 'timeoutString', not both");
rbta.setTimeoutString(timeoutString);
rbta.setReadOnly(attributes.getBoolean("readOnly")); // 只讀事務(wù)
rbta.setQualifier(attributes.getString("value")); // 事務(wù)管理器
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
// 回滾相關(guān)的設(shè)置
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
好了,到這里,判斷 BeanFactoryTransactionAttributeSourceAdvisor 這個 Advisor 能否應(yīng)用到當(dāng)前 Bean 的邏輯就完成了,如果能夠成功解析到事務(wù)屬性返回值就不為 null,那么方法匹配器就會返回 true,canApply() 方法也就返回了 true,也就代表這個 Advisor 是可以應(yīng)用到當(dāng)前 Bean 的,接下來就可以創(chuàng)建代理了。
我們可以回到上面 AbstractAutoProxyCreator 類的 wrapIfNecessary() 方法中看下,如果 getAdvicesAndAdvisorsForBean() 返回不為空,那么就要為當(dāng)前 Bean 創(chuàng)建代理,執(zhí)行 createProxy() 方法,方法邏輯如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
/**
* 下邊這塊邏輯主要是在判斷代理要基于類代理還是基于接口進(jìn)行代理
* 首先,檢查是否設(shè)置了 proxyTargetClass = true 屬性(即 CGLIB 代理),這個屬性的兩種設(shè)置方式:
* 注解方式:@EnableAspectJAutoProxy(proxyTargetClass = true)
* xml 配置方式:<aop:aspectj-autoproxy proxy-target-class="true" />
*/
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
// 在創(chuàng)建新的代理時,也要將該類實(shí)現(xiàn)的所有接口包含進(jìn)來,代理類要保持類的原有行為
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
// 如果開發(fā)時沒有明確要求使用 CGLIB 代理,也就是要走接口代理(JDK 代理)
else {
/**
* 檢查是否設(shè)置了 preserveTargetClass 屬性,這個屬性可通過 BeanDefinition 進(jìn)行設(shè)置
* 如果 BeanDefinition 中設(shè)置了該屬性,那么這里要使用 CGLIB 代理
* Tip:熟悉 Bean 生命周期的小伙伴兒們可能知道,我們可以通過 BeanFactoryPostProcessor 來
* 干預(yù) Bean 實(shí)例化過程,調(diào)整 Bean 的元數(shù)據(jù),也就是 BeanDefinition
*/
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
/**
* 評估是否使用接口代理
* 檢查目標(biāo)類有符合要求的接口:
* 如果有,則將目標(biāo)類的接口設(shè)置到代理工廠的 interfaces 屬性,執(zhí)行 JDK 動態(tài)代理
* 如果沒有,則設(shè)置 proxyTargetClass = true,執(zhí)行 CGLIB 代理
*/
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 將之前查找到的 Advisor 設(shè)置到代理工廠
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 可以通過繼承該類來自定義代理工廠,這里是預(yù)留的模板方法
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
// 生成代理對象并返回
return proxyFactory.getProxy(classLoader);
}
通過上面源碼分析得知,createProxy() 方法的主要任務(wù)是:
- 創(chuàng)建代理工廠,并根據(jù)用戶配置和類的接口信息等來判斷應(yīng)該使用 CGLIB 代理還是 JDK 代理來創(chuàng)建代理對象;
- 代理工廠使用已設(shè)置好的屬性參數(shù)來生成動態(tài)代理對象并返回。
再往后面就是使用具體的 AOP 代理工廠生成代理對象的邏輯了,這里我們不再繼續(xù)跟蹤源碼了,感興趣的小伙伴兒們可以自己再往后跟蹤分析一下。
其實(shí),執(zhí)行到這里的話,我們就知道了,在 Bean 的初始化過程中,Spring 已經(jīng)對包含 @Transactional 注解的 Bean 進(jìn)行了解析并生成了代理對象存儲到了容器中。
事務(wù)管理
既然代理對象已經(jīng)生成了,那么熟悉代理模式的小伙伴兒就知道了,我們在執(zhí)行目標(biāo)方法的時候就會導(dǎo)向到代理對象中定義的行為中去,具體如下:
- CGLIB 代理:會導(dǎo)向到 MethodInterceptor 接口的 intercept() 方法中
- JDK 代理:會導(dǎo)向到 InvocationHandler 接口的 invoke() 方法中
上述創(chuàng)建代理邏輯中通過 proxyFactory.getProxy(classLoader); 去獲取代理對象時,實(shí)際生產(chǎn)代理對象的代理類有兩個默認(rèn)實(shí)現(xiàn) CglibAopProxy 與 JdkDynamicAopProxy,具體執(zhí)行哪個類去生產(chǎn)代理對象也是通過 proxyTargetClass 屬性與目標(biāo)類是否實(shí)現(xiàn)接口來判斷的。下邊我們以 JdkDynamicAopProxy 這個代理工廠為例看下 invoke() 的核心執(zhí)行邏輯:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// ......
// 如果配置了 expose-proxy 屬性為 true,通過 ThreadLocal 保存到上下文中
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 獲取針對此方法的攔截器鏈
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 攔截器鏈為空,直接通過反射調(diào)用目標(biāo)方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 創(chuàng)建一個包含代理對象、目標(biāo)對象、方法、參數(shù)等信息的 MethodInvocation 實(shí)例
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 通過攔截器鏈執(zhí)行方法調(diào)用
retVal = invocation.proceed();
}
return retVal;
}
}
在 invoke() 方法的邏輯中,我們可以看到其核心步驟是獲取適用于目標(biāo)方法的攔截器鏈,并利用這個攔截器鏈作為參數(shù)來實(shí)例化一個 ReflectiveMethodInvocation 對象。隨后,通過調(diào)用該對象的 proceed() 方法,依次執(zhí)行攔截器鏈中的每個攔截器,執(zhí)行到最后調(diào)用我們的目標(biāo)方法,后面我們具體來分析這個執(zhí)行過程。
ReflectiveMethodInvocation 是 Spring AOP 內(nèi)部用于表示一次具體方法調(diào)用的對象,它封裝了實(shí)際的目標(biāo)方法調(diào)用,并且負(fù)責(zé)管理攔截器鏈的執(zhí)行流程。
我們首先來看下獲取攔截器鏈的邏輯,getInterceptorsAndDynamicInterceptionAdvice() 方法最終調(diào)用至 DefaultAdvisorChainFactory 類的同名方法中:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 先看類與切點(diǎn)是否匹配
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 獲取切點(diǎn)的方法匹配器
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
// 使用方法匹配器進(jìn)行方法匹配
match = mm.matches(method, actualClass);
}
if (match) {
// 匹配成功后,通過適配器將 Advisor 轉(zhuǎn)換成 MethodInterceptor
// 并將所有攔截器加入到攔截器鏈中
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
/**
* 如果是運(yùn)行時匹配,則創(chuàng)建一個新的動態(tài)方法匹配器實(shí)例并將其加入到攔截器鏈中
* 這個對象包含 MethodInterceptor 和 MethodMatcher 的實(shí)例
*/
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
// ......
// 返回構(gòu)建好的攔截器鏈
return interceptorList;
}
getInterceptorsAndDynamicInterceptionAdvice() 方法負(fù)責(zé)構(gòu)建適用于特定方法調(diào)用的攔截器鏈。該方法的主要任務(wù)是從 IOC 容器中獲取所有的 Advisor 對象,并根據(jù)目標(biāo)對象及其方法進(jìn)行篩選和適配,最終返回一個實(shí)現(xiàn)或者封裝了 MethodInterceptor 接口的對象組成的列表。
再來看下 proceed() 方法的邏輯:
public Object proceed() throws Throwable {
/**
* 索引變量,用來跟蹤當(dāng)前正在處理的攔截器位置。每次調(diào)用 proceed() 方法時,它都會增加,指向下一個要執(zhí)行的攔截器。
* 這里從索引為 -1 的攔截器開始調(diào)用按序遞增,并檢查是否已經(jīng)到達(dá)攔截器鏈的末尾
* 如果當(dāng)前索引已經(jīng)是最后一個攔截器,則通過反射直接調(diào)用目標(biāo)方法
*/
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 當(dāng)攔截器鏈中的所有攔截器都已經(jīng)被處理完畢后,調(diào)用此方法來執(zhí)行實(shí)際的目標(biāo)方法(即業(yè)務(wù)邏輯方法)。
// 這標(biāo)志著攔截器鏈的結(jié)束
return invokeJoinpoint();
}
// 獲取下一個要執(zhí)行的攔截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 如果是動態(tài)切點(diǎn)攔截器,需要在運(yùn)行時評估其是否應(yīng)該激活
// 靜態(tài)匹配部分已經(jīng)在構(gòu)建攔截器鏈時完成,并且匹配成功
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 使用方法匹配器匹配,判斷是否應(yīng)用此攔截器
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 匹配成功,則調(diào)用該攔截器的 invoke 方法,繼續(xù)執(zhí)行攔截器邏輯
return dm.interceptor.invoke(this);
}
else {
// 匹配失敗,跳過當(dāng)前攔截器,遞歸調(diào)用 proceed() 繼續(xù)下一個攔截器
return proceed();
}
}
else {
// 直接調(diào)用 MethodInterceptor 的 invoke 方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
proceed() 方法的作用就是按順序逐個執(zhí)行攔截器鏈中的攔截器。它從索引 -1 開始,逐個執(zhí)行攔截器,直至執(zhí)行到攔截器鏈的末尾后,完成對目標(biāo)方法的調(diào)用。
好的,分析了這么多,我們的代理對象到底是如何將事務(wù)管理起來的呢?
事務(wù)的實(shí)現(xiàn)也是通過攔截器來實(shí)現(xiàn)的,這個攔截器是 TransactionInterceptor,它實(shí)現(xiàn)了 MethodInterceptor接口。那么,當(dāng)目標(biāo)方法為事務(wù)方法時,proceed() 方法中調(diào)用的攔截器的 invoke() 方法會調(diào)用到 TransactionInterceptor 類中:
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 調(diào)用內(nèi)部方法 invokeWithinTransaction 來處理事務(wù)邏輯
// 回調(diào)接口用于提供具體的執(zhí)行邏輯,包括如何繼續(xù)執(zhí)行攔截器鏈或目標(biāo)方法,以及訪問目標(biāo)對象和方法參數(shù)等
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
// 繼續(xù)執(zhí)行攔截器鏈中的下一個元素
return invocation.proceed();
}
@Override
public Object getTarget() {
// 返回當(dāng)前調(diào)用的目標(biāo)對象實(shí)例
return invocation.getThis();
}
@Override
public Object[] getArguments() {
// 返回傳遞給目標(biāo)方法的參數(shù)數(shù)組
return invocation.getArguments();
}
});
}
可以看出,該方法處理帶有事務(wù)管理的目標(biāo)方法調(diào)用主要是通過內(nèi)部方法 invokeWithinTransaction() 實(shí)現(xiàn)的,這個方法會調(diào)用至其父類 TransactionAspectSupport 類中:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 如果沒有事務(wù)屬性,則該方法是非事務(wù)性的
TransactionAttributeSource tas = getTransactionAttributeSource();
// 計(jì)算事務(wù)屬性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 根據(jù)屬性配置獲取事務(wù)管理器,沒有配置獲取默認(rèn)事務(wù)管理器
final TransactionManager tm = determineTransactionManager(txAttr);
// ......
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // 聲明式事務(wù)處理
// 開啟事務(wù),將事務(wù)信息封裝成 TransactionInfo 對象
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 環(huán)繞通知,執(zhí)行目標(biāo)方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
/**
* 處理目標(biāo)方法調(diào)用期間拋出的異常
* 根據(jù)在 rollbackFor 中指定的回滾的異常類型匹配,決定事務(wù)回滾或者提交
*/
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清理事務(wù)信息
cleanupTransactionInfo(txInfo);
}
// ......
// 在正常返回后提交事務(wù)
commitTransactionAfterReturning(txInfo);
return retVal;
} else { // 編程式事務(wù)處理
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
// 根據(jù)事務(wù)屬性判斷是否需要回滾
if (txAttr.rollbackOn(ex)) {
// 如果是運(yùn)行時異常,直接拋出
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// 非回滾異常,記錄并返回 null
throwableHolder.throwable = ex;
return null;
}
}
finally {
// 清理事務(wù)信息
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
return result;
}
}
篇幅原因,我們就不再繼續(xù)深究事務(wù)的控制細(xì)節(jié)了。其實(shí)分析到這里的話,我們也基本了解了當(dāng)一個帶有 @Transactional 注解的方法被調(diào)用時,聲明式事務(wù)究竟是如何被控制的。
那就是,Spring AOP 機(jī)制會使用代理對象來攔截事務(wù)方法的執(zhí)行。最終通過 invokeWithinTransaction() 方法確保目標(biāo)方法在一個適當(dāng)?shù)氖聞?wù)上下文中運(yùn)行。它根據(jù) @Transactional 中配置的事務(wù)屬性選擇合適的事務(wù)管理器,并決定是否需要創(chuàng)建或加入現(xiàn)有事務(wù)。然后,在事務(wù)邊界控制邏輯中,代理對象會在執(zhí)行目標(biāo)方法前開啟事務(wù),在方法正常結(jié)束時提交事務(wù);如果過程中拋出了異常,則根據(jù)事務(wù)屬性配置的規(guī)則進(jìn)行回滾。整個過程自動完成了事務(wù)的開啟、提交和回滾。
總結(jié)
為了方便理解上述整個過程,請看下圖:
圖片