前言
在上一章我們分析了Spring的AOP的源碼,本篇文章是對事務的源碼分析,我們都知道事務的管理是基于AOP實作的,是以有了上一篇的鋪墊這一章會比較簡單一點。
事務的源碼我會分兩章寫,一張寫Transcational的解析,一張寫事務的執行流程。先上一個圖,待會兒可以根據這個圖來看源碼
事務配置案例
配置事務管理器,開啟注解事務支援
<!-- 開啟事務支援 -->
<tx:annotation-driven/>
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置資料源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...省略...
</bean>
标記方法需要事務
public class UserService ...{
//事務注解
@Transactional
public void insert(User user){
...
}
}
事務注解打在方法上,當然也是可以打在類上。
事務注解原理
在Spring中通常有兩種方式管理事務,一是使用XML統一配置事務 ,二是通過注解式程式設計即在對象方法上貼@Trancactional來管理事務,現在用的多的就是注解式程式設計,是以我們就從這個事務注解開始分析把。
Transactional貼在對象的方法上就可以通過AOP的方式幫我們開啟事務,送出事務或是復原事務操作,不僅簡化了我們的工作難度,避免重複事務邏輯處理,同時也減少了代碼的侵入,達到代碼解耦和的效果。
事務的管理是基于AOP實作,AOP的原理又是動态代理,也就是說被标記了@Transactional的類都會建立代理類來實作事務增強,建立代理後,方法的真實調用都是走代理對象。那麼Spring如何知道要對哪些類哪些方法進行代理?Spring會把 @Transactional 注解作為切點,這樣就知道哪些方法需要被代理 。至于代理類的建立是在Bean的執行個體化過程中完成,在上一篇文章有說道,見:《AOP原理》
事務标簽解析器
NamespaceHandlerSupport 注冊解析器
因為注解事務支援需要在XML中開啟 ,是以對于事務來說可以分為兩部分 :一是事務配置解析 ,二是代理建立。在上一章節我們就有提到,對于xml中的所有namespace都有對應的解析器, 在Spring中有一個接口叫 NamespaceHandlerSupport ,它提供了Spring xml配置的namespace的解析支援。
NamespaceHandlerSupport
它有一個子類
TxNaespaceHadler
就是針對于事務的 namespace 處理
public class TxNamespaceHandler extends NamespaceHandlerSupport {
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
//事務管理器預設的名字
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
static String getTransactionManagerName(Element element) {
return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
}
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}
AnnotationDrivenBeanDefinitionParser建立AutoProxyCreator
看得出來上面的 init 方法中注冊了一個
AnnotationDrivenBeanDefinitionParser
它就是針對于
<tx:annotation-driven/>
的解析器,并且負責建立事務管理需要的基礎類。
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
AnnotationDrivenBeanDefinitionParser() {
}
//解析元素:Element就是 <tx:annotation-driven/>
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注冊事務監聽
this.registerTransactionalEventListenerFactory(parserContext);
//判斷model,預設是proxy,走else
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
this.registerTransactionAspect(element, parserContext);
} else {
//建立自動代理建立器(InfrastructureAdvisorAutoProxyCreator)
AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
AopAutoProxyConfigurer 建立事務核心類
這個parse解析方法注冊了事務監聽器後就判斷了mode,預設使用的是proxy代理方式,即調用
configureAutoProxyCreator
方法,一路跟下去
/**
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
*/
private static class AopAutoProxyConfigurer {
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
//注冊一個自動代理建立器 InfrastructureAdvisorAutoProxyCreator
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
//判斷容器中是否已經注冊了 internalTransactionAdvisor
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
//【标記1】注冊 TransactionAttributeSource
// Create the TransactionAttributeSource definition.
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
//【标記2】注冊 TransactionInterceptor
// 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);
//【标記3】 注冊一個TransactionAttributeSourceAdvisor
// Create the BeanFactoryTransactionAttributeSourceAdvisor definition.
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
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);
}
}
}
該方法先是建立了一個
InfrastructureAdvisorAutoProxyCreator
代理建立器 ,然後又注冊了
AnnotationTransactionAttributeSource
,
TransactionInterceptor
,
BeanFactoryTransactionAttributeSourceAdvisor
三個Bean , 且前兩個Bean被添加到了
BeanFactoryTransactionAttributeSourceAdvisor
中。
- InfrastructureAdvisorAutoProxyCreator:是BeanPostProcessor的子類,在Bean的初始化之後調用postProcessAfterInitialization方法,目的就是找出增強器為合适的類建立代理。跟上一章節《AOP原理》做的事情差不多
-
AnnotationTransactionAttributeSource : 是專門用于擷取基于注解的Spring聲明式事務管理的事務屬性的屬性源,用來讀取@Transational注解并進行相關參數封裝,用做後續的事務處理。
它除了能處理Transactional注解之外,還支援JTA 1.2的javax.transaction.Transactional事務注解以及EJB3的javax.ejb.TransactionAttribute事務注解
- TransactionInterceptor:該類實作了TransactionAspectSupport , TransactionAspectSupport 中持有 TransactionManager ,擁有處理事務的能力。同時該類還實作了 MethodInterceptor 接口 ,它也作為AOP的攔截器。攔截器鍊中每個攔截器都有一個invoke方法,該方法就是對某個方法進行事務增強的入口,是以主要看invoke方法的實作邏輯!
-
BeanFactoryTransactionAttributeSourceAdvisor:它是一個Advisor,用來對事務方法做增強,隻要被注解@Transationl的方法都會被增強,該Advisor 包含 AnnotationTransactionAttributeSource 和 TransactionInterceptor ,以及 TransactionAttributeSourcePointcut 。
TransactionAttributeSourcePointcut 是事務屬性源比對器,是BeanFactoryTransactionAttributeSourceAdvisor的切入點,通過它來判斷某個bean是否可以被增強
解析目前方法是否要被代理
InfrastructureAdvisorAutoProxyCreator
是一個BeanPostProcessor,當Bean在執行個體化過程中,會通過AutoProxyCreator的
postProcessAfterInitialization
方法來決定是否建立代理,其實就是通過
BeanFactoryTransactionAttributeSourceAdvisor
來解析判斷該類是否被注解了@Transcational來決定是否建立代理 ,見:
AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
//擷取增強器 Advisors
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//建立代理
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
AbstractAdvisorAutoProxyCreator查找增強器
通過調用 AbstractAdvisorAutoProxyCreator#findCandidateAdvisors ,查找增強器 ,通過AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply 來比對目前類适合的增強器,然後為目前Bean建立代理。這個流程在上一章: 《AOP原理》 已經說過,這裡就不細細分析了。見:
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//查找所有增強器
List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
//找到目前beanClass适用的增強器
List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
this.extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
我們重點來看一下它是如何比對适合目前Bean的增強器的。見:
AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
//搜尋能應用于目前beanClass的 Advisors
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
這裡的 candidateAdvisors 就是擷取到的增強器 ,其中就包括:BeanFactoryTransactionAttributeSourceAdvisor , beanClass是目前要被建立代理的類,然後走的是AopUtils.findAdvisorsThatCanApply 方法查找能比對目前類的advisors.一路斷點跟下去會走到 AopUtils#canApply
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) {
//轉換成 PointcutAdvisor 切點的Advisor
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
AopUtils 查找合适的增強器
繼續跟蹤
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
源碼:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
//class 過濾器 ,預設直接傳回true
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//方法比對器,其實就是 BeanFactoryTransactionAttributeSourceAdvisor
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//擷取到要被建立代理的原生類的 class,并把其接口添加到classes中
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
//處理classes
for (Class<?> clazz : classes) {
//拿到原生類,或其接口的所有方法聲明
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
//通過 MethodMatcher 比對,走的是 TransactionAttributeSourcePointcut#matches方法
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
該方法的作用是比對給定的切入點是否完全适用于給定的類,通過MethodMatcher 來比對,這裡的MethodMatcher 本身就是
PointBeanFactoryTransactionAttributeSourceAdvisor
,它通過父類
TransactionAttributeSourcePointcut#matches
完成比對,如下:
TransactionAttributeSourcePointcut 查找@Transactional注解
下面是 TransactionAttributeSourcePointcut 進行比對的源碼:
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass) {
if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
//擷取 BeanFactoryTransactionAttributeSourceAdvisor 中的AnnotationTransactionAttributeSource
TransactionAttributeSource tas = getTransactionAttributeSource();
//判斷TransactionAttribute不為空,說明該targetClass的method是有事務注解的
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
這裡
getTransactionAttributeSource();
方法由子類
BeanFactoryTransactionAttributeSourceAdvisor
來實作,目的是擷取
TransactionAttributeSource
對象,然後調用tas.getTransactionAttribute擷取TransactionAttribute,不為空說明方法是支援@Transational的。
tas.getTransactionAttribute
方法中會先通過類名和方法名建構一個key,從緩存中 attributeCache 擷取TransactionAttribute,如果沒有,就通過
AnnotationTransactionAttributeSource#determineTransactionAttribute
去解析目前Method 的@Transactional封裝成 TransactionAttribute,并裝入緩存。源碼如下;
org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// First, see if we have a cached value.
//建構一個緩存key
Object cacheKey = getCacheKey(method, targetClass);
//從緩存中擷取TransactionAttribute
TransactionAttribute 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 {
//傳回緩存中的TransactionAttribute
return cached;
}
}
else {
// We need to work it out.
//查找和解析method的事務注解,封裝成TransactionAttribute
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
}
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
//把 TransactionAttribute 放入緩存attributeCache
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
我們看一下 computeTransactionAttribute 方法的源碼
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
//預設隻能是public方法進行事務代理,但是可以通過allowPublicMethodsOnly方法修改
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 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 = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
//【重要】首先從方法上找事務注解,有就傳回,沒有再找類上的事務注解,是以方法上的事務注解優先級高
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
//[重要]然後從目前類上找注解
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
//如果最終的目标方法和目前方法不一緻,從目前方法上找
if (specificMethod != method) {
//回退是看原方法
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
//最後一個回退是原始方法的類
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
//實在找不到,傳回一個null,該方法不會被代理
return null;
}
首先從方法上找事務注解,如果沒有就從類上面去找,如果始終找不到,就傳回一個null,該方法不會被代理。最終代碼來到
AnnotationTransactionAttributeSource#determineTransactionAttribute
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
//委托 TransactionAnnotationParser(SpringTransactionAnnotationParser) 去解析事務注解
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
查找事務屬性工作交給
AnnotationTransactionAttributeSource
完成,它委托了
TransactionAnnotationParser#parseTransactionAnnotation
來實作。預設使用解析器
SpringTransactionAnnotationParser
它支援Spring的@Transactional注解。
SpringTransactionAnnotationParser 解析 @Transcational注解
繼續跟蹤
SpringTransactionAnnotationParser#parseTransactionAnnotation
源碼如下:
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
//通過AnnotatedElementUtils查找 方法上的 Transactional注解,解析并封裝成AnnotationAttributes
//查找方法或者類上的@Transactional注解,沒找到,那麼會繼續在父類的方法上,和類上找。
//最終将使用找到的第一個注解資料
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
找到AnnotationAttributes 之後調用
parseTransactionAnnotation
進行解析,把事務的配置封裝成
RuleBasedTransactionAttribute
傳回,源碼如下
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
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;
}
總結
本篇文章主要是判斷類是否有被注解@Transcationl進而決定是否可用被代理,整體流程如下:
- 在 TxNamespaceHandler 注冊了AnnotationDrivenBeanDefinitionParser用來解析事務注解配置
- 在AnnotationDrivenBeanDefinitionParser中建立 InfrastructureAdvisorAutoProxyCreator,其本身是個BeanPostPorcessor,還建立了 TransactionAttributeSource , TransactionInterceptor , TransactionAttributeSourceAdvisor
- 在Bean執行個體化的過程中調用InfrastructureAdvisorAutoProxyCreator#postProcessAfterInitialization 來解析Bean是否有@Transational,進而判斷是否要進行增強建立代理。
- 在AutoProxyCreator 内部的 AopUtils#canApply 方法中通過 BeanFactoryTransactionAttributeSourceAdvisor的TransactionAttributeSourcePointcut切入點的matches方法中進行解析方法上的@Transactional 來決定是否要增強。
- 該方法擷取到BeanFactoryTransactionAttributeSourceAdvisor 中的AnnotationTransactionAttributeSource,調用其getTransactionAttribute來解析。内部通過 SpringTransactionAnnotationParser#parseTransactionAnnotation來解析 方法上的@Transactional,如果方法上沒有就去類上找,或者去父類的方法和類上找。
- 如果找到了就緩存到AbstractFallbackTransactionAttributeSource的attributeCache中,如果沒找到說明這類不需要被事務增強。
文章結束,喜歡就給個好評吧,你的肯定是我最大的動力哦!!!