Spring事务源码解析(二)获取增强
在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagement的实现逻辑
在进行接下来的源码解析之前我想大家应该知道,当我们使用传统的jdbc应用事务的时候是不是做了如下操作:
- 开启事务
- save、update、delete等操作
- 出现异常进行回滚
- 正常情况提交事务
而在Spring中我们好像只需要关心第三步,也就是我们的业务,而其他的操作都不需要关心。那么我想你应该猜到了Spring是如何实现的呢?
答案就是基于@Transactional注解的SpringAOP实现,在接着往下阅读本篇文章的时候希望您对于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事务源码解析(二)获取增强的更多相关文章
- spring事务源码解析
前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...
- Spring系列(六):Spring事务源码解析
一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...
- 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!
在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...
- spring 事务源码赏析(二)
我们在spring 事务源码赏析(一) 中分析了spring事务是如何找到目标方法,并如何将事务的逻辑织入到我们的业务逻辑中.本篇我们将会看到spring事务的核心实现: 1.事务传播机制的实现 2. ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- Spring系列(五):Spring AOP源码解析
一.@EnableAspectJAutoProxy注解 在主配置类中添加@EnableAspectJAutoProxy注解,开启aop支持,那么@EnableAspectJAutoProxy到底做了什 ...
- spring事务源码研读1
转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void test ...
- Spring IoC源码解析之getBean
一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...
- Spring IoC源码解析之invokeBeanFactoryPostProcessors
一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...
随机推荐
- SDL2学习(二):常用枚举值和函数
1. 高频枚举值或结构体 1.1 SDL_WindowFlags /** * \brief The flags on a window * * \sa SDL_GetWindowFlags() */ ...
- 面向对象程序设计(java)
201871010136-赵艳强<面向对象程序设计(java)>第二周学习总 第一部分:理论知识学习部分 一.简单的Java程序应运程序 1.标识符0标识符由字母.下划线.美元符号和数 ...
- hbase配置hdfs的HA之后需要修改的路径
hbase-site.xml中配置了hdfs的一个路径 <property> <name>hbase.rootdir</name> <value>hdf ...
- Mrmr:
Feature selection: minimum redundancy and maximum relevance feature selection for high-dimensional d ...
- VIJOS-P1045 Kerry 的电缆网络
JDOJ 1229: VIJOS-P1045 Kerry 的电缆网络 https://neooj.com/oldoj/problem.php?id=1229 Description K ...
- Spring Boot中以代码方式配置Tomcat
在Spring Boot2.0以上配置嵌入式Servlet容器时EmbeddedServletContainerCustomizer类不存在,经网络查询发现被WebServerFactoryCusto ...
- Flask-SQLAlchemy相关与Flask-Migrate相关
数据库按照一定规则保存应用数据,应用再发起查询,取回所需的数据.Web应用最常使用基于关系模型的数据库,这种数据库也称为SQL数据库,因为它们使用结构化查询语言SQL.不过近年来文档数据库和键 ...
- python的小数据池
一.什么是小数据池? 小数据池是一种缓存机制,也被称为驻留机制.各种编程语言中都有类似的东西(常量池.小数据池都是指得同一个内容). python自动将-5~256的整数.有一定规则的字符串.都放在一 ...
- impala进阶
一.impala存储 1.文件类型 2.压缩方式 二.impala分区 1.创建分区方式 partitioned by 创建表时,添加该字段指定分区列表: create table t_person( ...
- 后来,我发现,要想用好WIMBuilder2你必须具有以下能力:
后来,我发现,要想用好WIMBuilder2你必须具有以下能力:1.一定的中文语言阅读能力,两个帖子1500楼层中主要的功能要弄明白,包括wimbuilder2的架构.补丁的制作.2.一定的英文语言阅 ...