目录

  • 1. 前言
  • 2. 寻找事务类或方法对应的增强器
    • 2.1. `getAdvicesAndAdvisorsForBean()`
    • 2.2. 匹配增强器 `findAdvisorsThatCanApply()`
      • 2.2.1. 提取事务注解信息 `computeTransactionAttribute()`
    • 2.3. `invoke()`
      • 2.3.1. `MethodInterceptor` 的 `invoke()`
        • 2.3.1.1. `invokeWithinTransaction()` 重点
        • 2.3.1.2. 声明式事务处理主要有以下步骤
        • 2.3.1.3. 执行目标方法
        • 2.3.1.4. 回滚处理
  • 3. `Spring` 之事务小结
  • 4. `Spring` 之事务理论小结
    • 4.1. `Spring` 之事务执行流程
    • 4.2. `Spring` 之声明式事务与编程式事务

1. 前言

接着 Spring源码之事务(一)文章继续

2. 寻找事务类或方法对应的增强器

2.1. getAdvicesAndAdvisorsForBean()

Spring 通过 getAdvicesAndAdvisorsForBean() 获取指定 bean 对应的增强器,不但要找出增强器,而且还需要判断增强器是否满足要求

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {// 寻找所有合适的增强器 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();
}

寻找所有合适的增强器(增强器并不一定都适用于当前 bean,要选出满足我们通配符的增强器)

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 获取所有的增强List<Advisor> candidateAdvisors = findCandidateAdvisors();// 寻找所有增强中适用于 bean 的增强并应用List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 在advice链的开始添加ExposeInvocationInterceptorextendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}

上述方法的任务是:找出某个增强器是否适合于对应的类,而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性

2.2. 匹配增强器 findAdvisorsThatCanApply()

当找出所有的增强器后,接来的任务就是看这些增强器是否与 bean (比如 ProductInfoServiceImpl)对应的 class 匹配了,当然不只是 classclass 内部的方法如果能够匹配也可以

/*寻找所有增强中适用于目标bean的增强并应用*/
protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {/*设置当前要代理的beanName*/ProxyCreationContext.setCurrentProxiedBeanName(beanName);try {/*过滤已经得到的advisors*/return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);}finally {/*清除标记*/ProxyCreationContext.setCurrentProxiedBeanName(null);}
}/*主要功能是寻找所有增强器中适用于当前class的增强器*/
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}// 保存筛选的合适增强器List<Advisor> eligibleAdvisors = new ArrayList<>();// 首先处理引介增强for (Advisor candidate : candidateAdvisors) {// 核心匹配实现:canApply()if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}boolean hasIntroductions = !eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor) {// 引介增强已经处理continue;}// 如果不是IntroductionAdvisor实例,对于普通的增强,调用canApply()重载方法if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors;
}

当前我们分析的是对于 ProductInfoServiceImpl 是否适用于此增强方法,那么当前的 advisor 就是之前查找出来的类型为 BeanFactoryTransactionAttributeSourceAdvisorbean 实例。匹配合适的增强器核心步骤就是调用 canApply() 方法

public static boolean canApply(Advisor advisor, Class<?> targetClass) {return canApply(advisor, targetClass, false);
}public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {// 如果增强是引介增强,那么直接获取切点表达式去匹配targetClass即可if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}// 如果属于PointcutAdvisor,继续调用重载方法else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}// 其余的默认为符合条件else {return true;}
}

通过类的层次结构我们又知道:BeanFactoryTransactionAttributeSourceAdvisor 间接实现了 PointcutAdvisor。因此,在 canApply() 函数中的第二个 if 判断时就会通过判断,将 BeanFactoryTransactionAttributeSourceAdvisor 中的 getPointcut() 方法返回值作为参数继续调用 canApply() 方法

那么继续跟踪 canApply() 方法,targetClass 参数为 ProductInfoServiceImpl

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();if (methodMatcher == MethodMatcher.TRUE) {return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new LinkedHashSet<>();// 如果是代理类,则需要返回原始类if (!Proxy.isProxyClass(targetClass)) {classes.add(ClassUtils.getUserClass(targetClass));}classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);// 遍历类中的所有方法for (Method method : methods) {if (introductionAwareMethodMatcher != null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false;
}

通过上面函数大致可以理清大体脉络,首先对类本身进行遍历,如果不能确认是否匹配成功,那么就尝试获取类的所有接口,再获取类所有的方法,遍历每一个方法查看是否匹配,方法一旦匹配成功便认为这个类适用于当前增强器。匹配是通过 methodMatcher.matches() 方法实现的

@Override
public boolean matches(Method method, Class<?> targetClass) {// 判断targetClass是否是TransactionalProxy的子类或者子接口,是的话直接返回falseif (TransactionalProxy.class.isAssignableFrom(targetClass)) {return false;}TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}@Nullable
protected abstract TransactionAttributeSource getTransactionAttributeSource();

matches() 方法中定义了一个抽象模板函数,用于返回 TransactionAttributeSource 实例

调用 getTransactionAttribute(method, targetClass) 方法来返回一个 TransactionAttribute 事务属性对象,如果返回结果不为空,那么说明这是一个匹配的增强器

@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 如果是Object中定义的方法,返回nullif (method.getDeclaringClass() == Object.class) {return null;}// 首先查看缓存中是否有对应的处理结果Object cacheKey = getCacheKey(method, targetClass);Object cached = this.attributeCache.get(cacheKey);if (cached != null) {if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return (TransactionAttribute) cached;}}else {TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);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);}// 添加缓存this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}

getTransactionAttribute() 的函数主要目的:返回和 methodtargetClass 对应的 TransactionAttribute 事务属性。方法中包含着缓存逻辑,Spring 先尝试从缓存加载,如果不存在缓存,则通过 computeTransactionAttribute() 获取并添加到缓存。所以重点研究 computeTransactionAttribute() 方法。当然,需要注意的是,getTransactionAttribute() 的函数最终返回 txAttrtxAttr 可能为空,空的时候表明至少该方法没有事务,说明增强器对该方法不匹配。如果类中所有的方法都不匹配,那就表明增强器不匹配该类了

2.2.1. 提取事务注解信息 computeTransactionAttribute()

继续深入 computeTransactionAttribute(),该方法中其实就是解析事务注解配置信息的,看来事务注解的信息对应在 Spring 中的数据结构是 TransactionAttribute 接口。解析的规则:如果方法存在事务属性,则使用方法上的属性,否则使用方法所在的类上的属性;如果方法所在类的属性上还是没有搜寻到对应的事务属性,那么再搜寻接口中的方法,再没有的话,最后尝试搜寻接口的类上面的声明

当然,在开始对方法进行事务属性搜寻之前,Spring 对方法进行了判断,判断是否是 public 方法,如果不是直接返回空,也就是跳过该方法了。这也就解释我们之前介绍 Spring 配置的事务如果配置到非 public 方法上就不起效果了

@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 1.判断是否是 public 方法if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// method代表接口中的方法,specificMethod代表实现类中的方法Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// 2.首先查看方法是否存在事务声明TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 3.其次查看方法所在类是否存在事务声明txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}// 4.如果存在接口,则到接口中找if (specificMethod != method) {// 首先查看接口方法是否存在事务声明txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// 5.其次查看方法所在接口是否存在事务声明txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;
}

computeTransactionAttribute() 函数中并没有真正的去做搜寻事务属性的逻辑,而是将搜寻事务属性的任务委托给了 findTransactionAttribute() 去执行

// 寻找方法上是否存在事务声明
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);
}// 寻找类或接口是否存在事务声明
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);
}

可以看到,无论是对方法还是类或接口,最终都交由 determineTransactionAttribute(AnnotatedElement ae) 去完成解析

@Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {for (TransactionAnnotationParser annotationParser : this.annotationParsers) {TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);if (attr != null) {return attr;}}return null;
}

determineTransactionAttribute() 中,解析事务注解配置依靠 parse 解析类来帮助完成,这些 parse 解析类从代码上看应该是存放在 this.annotationParsers 中,而 this.annotationParsers 是当前类 AnnotationTransactionAttributeSource 中的一个 Set 集合成员

public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSourceimplements Serializable {····private final boolean publicMethodsOnly;private final Set<TransactionAnnotationParser> annotationParsers;public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;this.annotationParsers = new LinkedHashSet<>(2);this.annotationParsers.add(new SpringTransactionAnnotationParser());····}
}

可以看出在 AnnotationTransactionAttributeSource 初始化时,会给集合中添加一个解析类 SpringTransactionAnnotationParser 的实例。所以在 determineTransactionAttribute() 中其实委托了 SpringTransactionAnnotationParser 中的 parseTransactionAnnotation() 进行解析

@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(ae, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}
}

首先,先通过 AnnotatedElementUtilsfindMergedAnnotationAttributes() 解析 @Transactional 注解,返回 AnnotationAttributes,然后再利用 parseTransactionAnnotation() 对注解信息进行解析返回最终的事务属性实例

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();// 解析 propagationPropagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());// 解析 isolationIsolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());// 解析 timeoutrbta.setTimeout(attributes.getNumber("timeout").intValue());// 解析 readOnlyrbta.setReadOnly(attributes.getBoolean("readOnly"));// 解析 valuerbta.setQualifier(attributes.getString("value"));ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();// 解析 rollbackForClass<?>[] rbf = attributes.getClassArray("rollbackFor");for (Class<?> rbRule : rbf) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}// 解析 rollbackForClassNameString[] rbfc = attributes.getStringArray("rollbackForClassName");for (String rbRule : rbfc) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}// 解析 noRollbackForClass<?>[] nrbf = attributes.getClassArray("noRollbackFor");for (Class<?> rbRule : nrbf) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}// 解析 noRollbackForClassNameString[] nrbfc = attributes.getStringArray("noRollbackForClassName");for (String rbRule : nrbfc) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}rbta.getRollbackRules().addAll(rollBackRules);return rbta;
}

上面方法中实现了对对应类或者方法的事务注解的解析,你会在这个类中看到任何你常用或者不常用的属性提取

至此,我们终于完成了事务标签的解析。我们的任务是找出某个增强器是否适合于对应的类,而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性

现在,我们是以 ProductInfoServiceImpl 为例的,已经在它的接口上找到了事务注解,那么自然就返回了不为空的 TransactionAttribute 实例,所以它是与事务增强器匹配的,也就是它会被事务功能修饰

2.3. invoke()

由于我们从容器中获取的 ProductInfoServiceImplJDK 的一个代理对象。因为是 JDK 动态代理,当调用目标方法时,直接执行 JdkDynamicAopProxy (实现 InvocationHandler 接口)的 invoke() 方法

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;// 包含了原始类对象信息TargetSource targetSource = this.advised.targetSource;Object target = null;try {// 对 equals方法的处理if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {return equals(args[0]);}// 对 hashcode方法的处理else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {return hashCode();}else if (method.getDeclaringClass() == DecoratingProxy.class) {return AopProxyUtils.ultimateTargetClass(this.advised);}// 如果 method所在类是Advised父类,则直接调用切点方法else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;// 有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理至ThreadLocal中if (this.advised.exposeProxy) {// 将代理类对象proxy保存到ThreadLocal中,同时获取之前存储的oldProxyoldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// 获取当前方法的拦截器链(之前我们找的增强器统一封装成了拦截器链)List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {// 如果没有发现任何拦截器那么直接调用切点方法Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// 将拦截器封装在ReflectiveMethodInvocation,以便于使用下面的 proceed进行链接调用拦截器invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// 执行拦截器链中每个拦截器的 invoke 方法retVal = invocation.proceed();}Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal = proxy;}else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {AopContext.setCurrentProxy(oldProxy);}}
}

getInterceptorsAndDynamicInterceptionAdvice() 方法获得的拦截器调用链 chain 内容其实只有 TransactionInterceptor 一个,TransactionInterceptor 实现了 MethodInterceptor 接口。最终会调用每个 MethodInterceptorinvoke()

2.3.1. MethodInterceptorinvoke()

随后进行拦截器的逐一调用,调用的其实是:执行拦截器中的 invoke() 方法

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

targetClass 就是 ProductInfoServiceImpl 的全限定类名

2.3.1.1. invokeWithinTransaction() 重点

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// 1. 获取对应配置的事务属性,事务属性保存在 AnnotationTransactionAttributeSource中TransactionAttributeSource tas = getTransactionAttributeSource();final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// 2. 获取 beanFactory 中的 transactionManagerfinal PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);// 3. 对不同的事务处理方式使用不同的逻辑,包括声明式和编程式// 声明式事务处理if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 4. 创建 TransactionInfo,在目标方法执行前获取事务并收集事务信息TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 5. 执行被增强方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 6. 异常回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 7. 清除信息cleanupTransactionInfo(txInfo);}// 8. 提交事务commitTransactionAfterReturning(txInfo);return retVal;}// 编程式事务处理else {final ThrowableHolder throwableHolder = new ThrowableHolder();try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {throwableHolder.throwable = ex;return null;}}finally {cleanupTransactionInfo(txInfo);}});if (throwableHolder.throwable != null) {throw throwableHolder.throwable;}return result;}catch (ThrowableHolderException ex) {throw ex.getCause();}catch (TransactionSystemException ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);ex2.initApplicationException(throwableHolder.throwable);}throw ex2;}catch (Throwable ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);}throw ex2;}}
}

该方法就是我们需要着重研究的对象,我们整理下事务处理的脉络,在 Spring 中支持两种事务处理的方式,分别是声明式事务处理与编程式事务处理,两者相对于开发人员来讲差别很大,考虑到对事务的应用比声明式的事务处理使用起来方便,也相对流行些,我们就分析声明式事务

2.3.1.2. 声明式事务处理主要有以下步骤

  1. 获取事务的属性。对于事务处理来说,最基础或者说最首要的工作便是获取事务属性了,这是支撑整个事务功能的基石,在前面我们已经分析了事务属性提取
  2. 加载配置文件中配置的 TransactionManager
  3. 不同的事务处理方式使用不同的逻辑。主要是声明式事务和编程式事务之分
  4. 创建 TransactionInfo,在目标方法执行前获取事务并收集事务信息
  5. 执行目标(被代理类的)方法。注意该方法的调用时机
  6. 一旦出现异常,尝试异常处理。并不是所有异常,Spring 都会将其回滚,默认只对 RuntimeExceptionError 两种情况会进行回滚处理
  7. 提交事务前将事务信息清除
  8. 提交事务

对于 8 步以上详情:https://blog.csdn.net/bskfnvjtlyzmv867/article/details/83960279

2.3.1.3. 执行目标方法

这一部分在 Aop 中已经分析过,主要是进行事务拦截器调用完就执行目标本身方法

2.3.1.4. 回滚处理

一旦出现 Throwable 就会被引导至 completeTransactionAfterThrowing() 处理

try {// 5. 执行被增强方法retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {// 6. 异常回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;
}

处理异常不代表所有的 Throwable 都会被回滚处理,比如我们最常用的 Exception 默认是不会被处理的。默认情况下,即使出现异常,数据也会被正常提交,而是否回滚,关键就是在 txInfo.transactionAttribute.rollbackOn(ex) 这个函数

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {// 当抛出异常时首先判断当前是否存在事务,这是基础依据if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}// 这里判断是否回滚默认的依据是抛出的异常是否是RuntimeException或者是Error的类型if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {// 根据TransactionStatus信息进行回滚处理txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}else {// 如果不满足回滚条件即使抛出异常也同样会提交try {txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by commit exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}}}
}

Spring 的事务回滚条件

  • 如果事务注解中配置有 rollbackForrollbackForClassName,则进行遍历,如果符合规则,则返回 true,即事务需要回滚
  • 如果事务注解中没有配置回滚规则,默认情况下 Spring 只对 RuntimeExceptionError 两种情况会进行回滚处理

3. Spring 之事务小结

  • Spring 容器初始化启动时,会去解析所有的配置文件,将配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory
  • ==同时,在 Spring 容器初始化启动时,会完成向 IOC 容器中注册 bean 的工作。在注册的过程中,会去判断这些 bean 需不需要被代理(获取 bean 的事务或非事务增强器,有则需要代理,反之,不需要代理)
  • 如果不需要,则直接返回原始单例 bean,此时单例 bean 已完成向 IOC 容器的注册工作
  • 如果需要,再确定代理方式是使用 JDK 代理,还是使用 Cglib,去创建代理对象Spring Aop 代理对象的产生详情可以查看
  • 当使用被代理对象调用被代理类的方法时,实际上是使用代理对象调用 invoke() 方法,也就是调用到了声明式的事务相关的处理(invokeWithinTransaction()方法是 Spring 事务管理的核心)

4. Spring 之事务理论小结

4.1. Spring 之事务执行流程

在调用声明 @Transactional 的目标方法或类时,Spring 默认使用 Aop,在代码运行时生成一个代理对象,根据 @Transactional 的属性配置信息,由代理对象决定声明 @Transactional 的目标方法是否被拦截器 TransactionInterceptor 来拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建或者加入事务,并执行目标方法的逻辑,,最后根据执行情况是否出现异常,利用事务管理器 PlatformTransactionManager 操作数据源 DataSource 提交或回滚事务

4.2. Spring 之声明式事务与编程式事务

  • 声明式事务:它建立在 Aop 之上,本质是对目标方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
  • 编程式事务使用 TransactionTemplate 或直接使用底层 PlatformTransactionManager 手动通过代码为方法添加事务

声明式事务最大的优点:就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于 @Transactional 注解的方式),便可以将事务规则应用到业务逻辑中

参考:https://blog.csdn.net/bskfnvjtlyzmv867/article/details/83960279

Spring源码之事务(二)相关推荐

  1. Spring源码之ResourceLoader(二):PathMatchingResourcePatternResolver实现getResources加载多文件

    Spring源码之ResourceLoader二:PathMatchingResourcePatternResolver实现getResources加载多文件 findAllClassPathReso ...

  2. Spring源码系列(十二)Spring创建Bean的过程(二)

    1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...

  3. Spring源码分析(二):底层架构核心概念解析

    本节主要介绍一下Spring底层中用到的"基础设施",是后续看Spring源码所必备的,防止后续看源码的过程中,遇到不会的概念得单独跳出来学习. BeanDefinition Be ...

  4. idea 编译spring_《Spring源码解析(二)》构建 Spring5 源码工程,开启研读Spring源码之路...

    Spring5 源码下载注意事项 首先你的 JDK 需要升级到 1.8 以上.Spring3.0 开始,Spring 源码采用 github 托管,不再提供官网下载 链接.这里不做过多赘述,大家可自行 ...

  5. Spring源码分析(二)BeanFactoryPostProcessor之ConfigurationClassPostProcessor的调用过程

    前言:BeanFactoryPostProcessor是Spring中的重要组成接口,容器创建过程中起到解析注解,注册BeanDifinition,改变BeanDefiniton属性(也就是改变bea ...

  6. spring源码解析(二) @Autowired自动注入过程

    1.依赖注入到底有几种?两种.四种.五种? 两种: 手动:set(byType.byName).构造器 自动:xml中:set.构造器 autowired注解中:set.属性.构造器 重点不在于到底有 ...

  7. Spring源码解析(二)BeanDefinition的Resource定位

    IOC容器的初始化过程主要包括BeanDefinition的Resource定位.载入和注册.在实际项目中我们基本上操作的都是ApplicationContex的实现,我们比较熟悉的ClassPath ...

  8. 《Spring源码深度解析 郝佳 第2版》事务

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  9. Spring源码解析之-- 事务TransactionInterceptor 分析(开启事务)

    目录 一.介绍 二.TransactionInterceptor 分析 2. 流程 2.1 invoke 2.1.1 TransactionAspectSupport#invokeWithinTran ...

  10. Spring 源码分析衍生篇十三 :事务扩展机制 TransactionSynchronization

    文章目录 一.前言 二.TransactionSynchronization 1. TransactionSynchronization 1.1 TransactionSynchronization ...

最新文章

  1. 第一阶段用户模板和场景
  2. Json.NET Deserialize时如何忽略$id等特殊属性
  3. c语言编写心理测试,求各位大神赐教!我做了一个“心理测试的答题卷”编程,总共有1...
  4. Linux 2.6.32-279.el6.x86_64 ANDROID SDK碰到”LIBC.SO.6: VERSION `GLIBC_2.14′ NOT FOUND”的解决方法(1)
  5. Android如何给无法更改继承关系的Activity更换ActionBar(setContentView方法实战)
  6. 51单片机之特殊功能寄存器SFR
  7. 英国国家网络安全中心:速修复严重的 MobileIron RCE 漏洞 (CVE-2020-15505)
  8. Pikachu实验过程3(XSS的分析)
  9. 痕迹清理 - Linux
  10. 影响世界的77部文学名著
  11. Cocos2d-xV3.17.2获取csb文件按钮组件并绑定事件
  12. 通过SSH终端管理ESXI虚拟机
  13. java操作跨页的word cell_Java 创建Word表格/嵌套表格、添加/复制表格行或列、设置表格跨页断行...
  14. EBC金融外汇原油专题|权威解读「七大因素主导全球原油价格走势」
  15. java 刻度尺_GitHub - yhongm/ScaleView: ArcScaleView,ScaleView,刻度尺选择器,包括弧形刻度尺选择器和直尺形刻度尺选择器...
  16. 高级Android工程师面试回忆录
  17. 基于Android的校园跑腿系统
  18. 一句话木马@eval($_POST[“cmd“]);是什么意思(超详细)
  19. 网上税务html模板,网上税务申报系统
  20. html5音乐加大音量,视频背景音乐音量增大(增大视频音量)的方法

热门文章

  1. 极客大学架构师训练营 大数据平台、Sqoop、Canal、Flume、物联网平台 第27课 听课总结
  2. linux mysql 密码策略_Linux Ubuntu 14 MySQL 密码策略(复杂度)和审计插件
  3. Climbing Stairs @python
  4. 线性回归模型的评价指标
  5. K-Means与KNN比较
  6. python获取程序运行路径
  7. 反射方法返回值是数组时取其中元素的办法
  8. 斯坦福大学自然语言处理第三课“最小编辑距离(Minimum Edit Distance)”
  9. 在VMware安装Ubuntu后一直停留在VMware Easy Install
  10. 一种一致性HASH算法的实现方法,附核心代码