在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagement的实现逻辑

在进行接下来的源码解析之前我想大家应该知道,当我们使用传统的jdbc应用事务的时候是不是做了如下操作:

  1. 开启事务
  2. save、update、delete等操作
  3. 出现异常进行回滚
  4. 正常情况提交事务

而在Spring中我们好像只需要关心第三步,也就是我们的业务,而其他的操作都不需要关心。那么我想你应该猜到了Spring是如何实现的呢?

答案就是基于@Transactional注解的SpringAOP实现,在接着往下阅读本篇文章的时候希望您对于SpringAOP的源码有一定的了解,如果不了解可以参考如下文章:

  1. 基于注解的SpringAOP源码解析(一)
  2. 基于注解的SpringAOP源码解析(二)
  3. 基于注解的SpringAOP源码解析(三)
获取增强

在阅读完AOP的原理之后,我们知道,当一个bean实例化之后会尝试获取所有适用于此Bean的增强。而在上篇文章中,我们已经发现了,@EnableTransactionManagement注解会往Spring中注入一个增强BeanFactoryTransactionAttributeSourceAdvisor。经过一番代码调用以后,会进入这么一个方法,这里的第一个入参就是BeanFactoryTransactionAttributeSourceAdvisor增强

	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 pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
return true;
}
}
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<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(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;
}

到这里的时候就进入事务相关的类TransactionAttributeSourcePointcut,看名字就能知道,这是个切点类.那么接下来的逻辑应该可以想象到,无非就是判断是否是个事务方法

public boolean matches(Method method, @Nullable Class<?> targetClass) {
if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

如果是事务方法则继续往下走

public TransactionAttribute getTransactionAttribute(Method method,
@Nullable Class<?> targetClass) {
// 如果当前方法是Object类中的方法,则直接返回
if (method.getDeclaringClass() == Object.class) {
return null;
} // 获取当前方法缓存使用的key
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);
// 如果生成的事务属性是DefaultTransactionAttribute类型的,则将方法签名设置到其descriptor属性中
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;
}
}

接着看一下方法的事务属性是如何解析的

protected TransactionAttribute computeTransactionAttribute(Method method,
@Nullable Class<?> targetClass) {
// 如果设置了只对public方法进行事务代理,并且当前方法不是public的,则返回null
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
} Class<?> userClass = (targetClass != null ?
ClassUtils.getUserClass(targetClass) : null);
// 获取最为准确的方法,即如果传入的method只是一个接口方法,则会去找其实现类的同一方法进行解析
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
// 如果当前方法是一个泛型方法,则会找Class文件中实际实现的方法
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 解析目标方法,获取其是否存在事务属性,如果存在则直接返回
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
} // 解析目标方法所在的类,判断其是否标注有事务属性,如果存在,并且目标方法是用户实现的方法,则直接返回
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
} // 如果通过解析到的方法无法找到事务属性,则判断解析得到的方法与传入的目标方法是否为同一个方法,
// 如果不是同一个方法,则尝试对传入的方法及其所在的类进行事务属性解析
if (specificMethod != method) {
// 对传入方法解析事务属性,如果存在,则直接返回
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
} // 对传入方法所在类进行事务属性解析,如果存在,则直接返回
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
} return null;
}

这里对事务属性的解析主要分为对目标方法进行解析和对传入方法进行解析,接着看findTransactionAttribute方法

protected TransactionAttribute findTransactionAttribute(Method method) {
return determineTransactionAttribute(method);
}
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
return attr;
}
}
return null;
}
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
ae, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
// 判断目标方法上是否存在@Transactional注解,如果不存在,则直接返回
AnnotationAttributes attributes = AnnotatedElementUtils
.findMergedAnnotationAttributes(ae, Transactional.class, false, false);
if (attributes != null) {
// 如果目标方法上存在@Transactional注解,则获取注解值,并且封装为TransactionAttribute返回
return parseTransactionAnnotation(attributes);
} else {
return null;
}
} protected TransactionAttribute parseTransactionAnnotation(
AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 获取注解上的propagation值
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
// 获取注解上的isolation属性值
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
// 获取注解上的timeout属性值
rbta.setTimeout(attributes.getNumber("timeout").intValue());
// 获取注解上的readOnly属性值
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 获取注解上的value属性值
rbta.setQualifier(attributes.getString("value"));
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();
// 获取注解上的rollbackFor属性列表
Class<?>[] rbf = attributes.getClassArray("rollbackFor");
for (Class<?> rbRule : rbf) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
// 获取注解上的rollbackForClassName属性列表
String[] rbfc = attributes.getStringArray("rollbackForClassName");
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
// 获取注解上的noRollbackFor属性列表
Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
for (Class<?> rbRule : nrbf) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
// 获取注解上的noRollbackForClassName属性列表
String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);
return rbta;
}

可以看到这里已经把方法上或者类上的@Transactional注解的属性封装成TransactionAttribute返回了,关于@Transactional注解的更多知识可参考我的这篇文章:

Spring@Transactional注解

Spring事务源码解析(二)获取增强的更多相关文章

  1. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  2. Spring系列(六):Spring事务源码解析

    一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...

  3. 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

    在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...

  4. spring 事务源码赏析(二)

    我们在spring 事务源码赏析(一) 中分析了spring事务是如何找到目标方法,并如何将事务的逻辑织入到我们的业务逻辑中.本篇我们将会看到spring事务的核心实现: 1.事务传播机制的实现 2. ...

  5. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  6. Spring系列(五):Spring AOP源码解析

    一.@EnableAspectJAutoProxy注解 在主配置类中添加@EnableAspectJAutoProxy注解,开启aop支持,那么@EnableAspectJAutoProxy到底做了什 ...

  7. spring事务源码研读1

    转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void test ...

  8. Spring IoC源码解析之getBean

    一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...

  9. Spring IoC源码解析之invokeBeanFactoryPostProcessors

    一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...

随机推荐

  1. 201871010124-王生涛 《面向对象程序设计(java)》第八周学习总结

    博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...

  2. IDEA写vue项目出现红色波浪线警告如何解决??

    1.看图 2.希望对大家有帮助,只要修改了这个就可以,如有任何问题都可以留言,谢谢大家 2019-09-1923:54:11 作者:何秀好

  3. JAVA大数的一些操作

    参考: https://www.cnblogs.com/tonyyy/p/10433460.html https://www.cnblogs.com/wkfvawl/p/9377441.html (d ...

  4. 莫烦TensorFlow_01 基本程序结构

    import tensorflow as tf import numpy as np # create data x_data = np.random.rand(100).astype(np.floa ...

  5. Slack完整教学与上手心得:找到正确的团队沟通之道

    Slack完整教学与上手心得:找到正确的团队沟通之道 时间 2015-06-13 09:21:42 逐鹿网 原文  http://www.zhulu.com/article/5519.html 主题 ...

  6. echars 实现多曲线,多Y轴,曲线单选切换,并且跟随切换指定Y轴

    html代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  7. 学习:反调试之ZwQueryInformationProcess

    SetUnhandledExceptionFilter触发条件:1.当程序有异常,且没相应的处理 2.没有人进行反调试,满足这两个条件的时候,就会其中传输的参数(实则就是一个异常处理函数) 来进行处理 ...

  8. ajax与重定向

    网上有不少说法ajax的请求url浏览器不会重定向的说法是片面的,正常是这样的: 当服务器将302响应发给浏览器时,浏览器并不是直接进行ajax回调处理,而是先执行302重定向——从Response ...

  9. 【HDU6327】Random Sequence(记忆化搜索)

    点此看题面 大致题意: 给你两个序列\(a,v\),其中\(a\)数组由\(0\sim m\)组成.随机用\(1\sim m\)中的一个数替换\(a\)中的\(0\),求\(\sum_{i=1}^{n ...

  10. mysql 类型自动化转换问题

    mysql 类型自动化转换问题 背景  有个业务需求,使用到find_in_set函数,简单贴下,如下: SELECT FIND_IN_SET('b','a,b,c,d'); //返回值为2,即第2个 ...