本文内容

  1. @Transactional事务使用

  2. @EnableTransactionManagement 详解

  3. @Transactional事务属性的解析

  4. TransactionInterceptor 事务控制

声明式事务使用和原理

声明式的主要步骤

  1. 使用@EnableTransactionManagement启用Spring 事务管理支持
  2. 使用@Transactional标识需要事务的方法会自动开启事务
  3. 注入数据源和事务管理器

下面通过案例演示一下上面的效果。

案例

  1. 使用@EnableTransactionManagement启用Spring 事务管理支持,配置类上需要有@Configuration注解

    @Configuration
    @ComponentScan
    @EnableTransactionManagement
    public class AppConfig {}
  2. 使用@Transactional标识需要事务的方法会自动开启事务。addUser方法需要事务

    @Service
    public class UserService { @Autowired
    private JdbcTemplate jdbcTemplate; @Transactional
    public void addUser() {
    System.out.println("执行前记录:" + jdbcTemplate.queryForList("SELECT * from t_user"));
    jdbcTemplate.update("insert into t_user (name) values (?)", "xx");
    jdbcTemplate.update("insert into t_user (name) values (?)", "oo");
    System.out.println("执行后记录:" + jdbcTemplate.queryForList("SELECT * from t_user"));
    }
    }
  3. 注入数据源和事务管理器

    @Configuration
    @ComponentScan
    @EnableTransactionManagement
    public class AppConfig { /**
    * 定义一个数据源
    * @return
    */
    @Bean
    public DataSource dataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://192.168.25.24:3306/xxx?characterEncoding=UTF-8");
    dataSource.setUsername("root");
    dataSource.setPassword("xxx");
    return dataSource;
    } /**
    * 定义一个JdbcTemplate来执行sql
    * @param dataSource
    * @return
    */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
    } /**
    * 定义一个管理器
    * @param dataSource
    * @return
    */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
    }
    }
  4. 测试程序

    public class DeclarativeTest {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext context =
    new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = context.getBean(UserService.class);
    userService.addUser();
    context.close();
    }
    }

    输出结果如下:

    执行前记录:[{id=1, name=test1-1}, {id=2, name=test1-2}, {id=3, name=xx}, {id=4, name=oo}, {id=5, name=xx}, {id=6, name=oo}]
    执行后记录:[{id=1, name=test1-1}, {id=2, name=test1-2}, {id=3, name=xx}, {id=4, name=oo}, {id=5, name=xx}, {id=6, name=oo}, {id=7, name=xx}, {id=8, name=oo}]

原理

@EnableTransactionManagement注解会开启Spring自动管理事务的功能。开启之后在Spring容器启动的过程中,会拦截所有bean的创建过程,判断bean 是否需要让Spring来管理事务,如果需要那么通过aop的方式创建代理对象。代理中会添加一个拦截器TransactionInterceptor,拦截@Trasaction标识方法的执行,在方法执行前后添加事务的功能。

下面进行源码分析,需要用到的前置只是是Spring Aop相关知识和编程式事务管理的知识,前面的文章有涉及,提前看一下。

@EnableTransactionManagement 详解

@EnableTransactionManagement 会开启Spring的事务管理功能,查看下源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement { /**
* 指示是否创建基于子类(CGLIB)的代理(true),而不是标准的基于Java接口的代理(false)。默认为false。
* 仅当mode()设置为AdviceMode.PROXY时适用。
*/
boolean proxyTargetClass() default false; /**
* 指示应该如何应用事务通知。 默认值是AdviceMode.PROXY。
* 请注意,代理模式只允许通过代理拦截调用。同一类内的本地调用不会被拦截;
* 在本地调用中,对这种方法的Transactional注释将被忽略,因为Spring的拦截器甚至不会在这样的运行时场景中起作用。
*/
AdviceMode mode() default AdviceMode.PROXY; /**
* 当在特定连接点上应用多个通知时,指示事务顾问程序的执行顺序。 默认值是Ordered.LOWEST_PRECEDENCE
*/
int order() default Ordered.LOWEST_PRECEDENCE; }

3个参数属性值

  • proxyTargetClass : 指示是否创建基于子类(CGLIB)的代理(true),而不是标准的基于Java接口的代理(false)
  • mode:指示应该如何应用事务通知。 默认值是AdviceMode.PROXY。
  • order: 当在特定连接点上应用多个通知时,指示事务顾问程序的执行顺序。 默认值是Ordered.LOWEST_PRECEDENCE,最后处理事务拦截器。

TransactionManagementConfigurationSelector

重点是@Import(TransactionManagementConfigurationSelector.class),注入一些事务相关的bean到Spring容器中进行事务的管理控制。

根据EnableTransactionManagement的mode值选择应该使用AbstractTransactionManagementConfiguration的哪个实现。

package org.springframework.transaction.annotation;

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

   /**
* 此处是AdviceMode的作用,默认是用代理,另外一个是ASPECTJ
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// @1
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
} private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
} }

我们的mode是 AdviceMode.PROXY ,所以走@1位置,注入AutoProxyRegistrarProxyTransactionManagementConfiguration

AutoProxyRegistrar注入 InfrastructureAdvisorAutoProxyCreator

AutoProxyRegistrar的作用是注入一个InfrastructureAdvisorAutoProxyCreator,用于拦截bean的创建过程,为需要的事务控制的bean 创建代理对象,这个类非常关键,后面详细讲。

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
// 遍历所有注解,找到有mode和proxyTargetClass的注解
for (String annType : annTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
// 注册aop InfrastructureAdvisorAutoProxyCreator 不展开
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
// 强制设置proxyTargetClass=true后面使用cglib
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
} }

ProxyTransactionManagementConfiguration

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
} @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
} @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
} }

ProxyTransactionManagementConfiguration 代理事务配置,注册事务需要用的一些类,而且Role=ROLE_INFRASTRUCTURE都是属于内部级别的,如下:

  • BeanFactoryTransactionAttributeSourceAdvisor 事务属性通知器,存放事务注解的方法相关的属性
  • TransactionAttributeSource事务属性源,就是事务注解的一些属性,也用来解析事务注解属性,实际是AnnotationTransactionAttributeSource
  • TransactionInterceptor事务拦截器,该类包含与Spring底层事务API的集成。TransactionInterceptor简单地以正确的顺序调用相关的超类方法,比如invokeWithinTransaction。这个类非常关键,负责事务相关的AOP增强的。

小结

EnableTransactionManagement注解的作用主要注入了InfrastructureAdvisorAutoProxyCreator负责拦截bean的创建过程为特定的bean创建代理对象,并通过TransactionInterceptor事务拦截器来实现方法的事务控制。

@Transactional 详解

该注解用于描述单个方法或类上的事务属性。在类级别,该注释作为默认值应用于声明类及其子类的所有方法。注意,类级别它并不适用于类层次结构上的父类,也就是父类方法需要在本地重新声明,以便参与子类级别的注释。

注解的属性的语义的具体信息,由 TransactionDefinitionTransactionAttribute 提供。

package org.springframework.transaction.annotation;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional { @AliasFor("transactionManager")
String value() default ""; /**
* 用来确定目标事务管理器bean
*/
@AliasFor("value")
String transactionManager() default ""; /**
* 事务传播类型
*/
Propagation propagation() default Propagation.REQUIRED; /**
* 事务隔离级别
*/
Isolation isolation() default Isolation.DEFAULT; /**
* 事务超时
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; /**
* 只读事务
*/
boolean readOnly() default false; /**
* 指定哪些异常类型必须导致事务回滚,指定Throwable的子类型;默认只回滚RuntimeException和Error
*/
Class<? extends Throwable>[] rollbackFor() default {}; /**
* 指示哪些异常类型必须导致事务回滚,这里异常类名称
*/
String[] rollbackForClassName() default {}; /**
* 指定哪些异常不进行回滚
*/
Class<? extends Throwable>[] noRollbackFor() default {}; /**
* 指定哪些异常类型不进行回滚,异常类型名称
*/
String[] noRollbackForClassName() default {}; }

@Transactional注解如何解析成事务属性

AnnotationTransactionAttributeSource类

从类图看我们关注AnnotationTransactionAttributeSource通过SpringTransactionAnnotationParser@Transcation转成事务属性供Spring事务处理使用。

  1. 如果@Transcation注解配置了属性,转换成RuleBasedTransactionAttribute
  2. 如果@Transcation注解没有配置属性,转换成DefaultTransactionAttribute,只有在抛出RuntimeExceptionError时候才回滚

按照Spring源码设计设计的一般套路我们看下右侧的TransactionAttributeSource 接口和抽象类AbstractFallbackTransactionAttributeSource

TransactionAttributeSource 接口

public interface TransactionAttributeSource {

   /**确定给定的类是否是TransactionAttributeSource元数据格式的事务属性的候选类*/
default boolean isCandidateClass(Class<?> targetClass) {
return true;
} /**解析给定方法的@Transaction事务属性,如果方法是非事务性的,则返回null*/
@Nullable
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass); }

AbstractFallbackTransactionAttributeSource类

先看getTransactionAttribute()方法

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 判断method所在的class是不是Object类型
if (method.getDeclaringClass() == Object.class) {
return null;
} // First, see if we have a cached value.
// 构建缓存key
Object cacheKey = getCacheKey(method, targetClass);
// 从缓存中获取 @1
TransactionAttribute cached = this.attributeCache.get(cacheKey);
// 有缓存,不会每次computeTransactionAttribute
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 {
// 存在就直接返回事务属性
return cached;
}
}
else {
// We need to work it out.
// 查找我们的事务注解
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.isTraceEnabled()) {
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
// 加入缓存
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}

缓存中有类对应方法的事务属性就直接返回,没有就先解析@1再缓存起来。

computeTransactionAttribute方法

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// 首先判断方法是否是public,默认是支持public的
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代表实现类中的方法
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;
}
} return null;
}

查找事务属性的查找顺序如下:

  1. 特定的目标方法,会去找父类或者接口的方法
  2. 目标类,会去找父类或者接口
  3. 声明的方法
  4. 声明方法所在的类

AnnotationTransactionAttributeSource#determineTransactionAttribute() 方法委托给SpringTransactionAnnotationParser解析给定类或是方法上的@Transactional注解的事务属性

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
// 获取我们的注解解析器
for (TransactionAnnotationParser parser : this.annotationParsers) {
// 通过注解解析器去解析我们的元素(方法或者类)上的注解
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}

接下来看下是如何解析和包装的SpringTransactionAnnotationParser#parseTransactionAnnotation()方法。

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
// 从element对象中获取@Transactional注解,然后把注解属性封装到了AnnotationAttributes
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
// 解析出真正的事务属性对象
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}

分析2个点:

  • AnnotatedElementUtils.findMergedAnnotationAttributes()负责解析目标类或目标方法上的@Transactional,会向上找父类或是接口的
  • parseTransactionAnnotation()方法包装成TransactionAttribute

看下是如何包装转换的。

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {

   // 创建一个基础规则的事务属性对象
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); // 解析@Transactionl上的传播行为
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
// 解析@Transactionl上的隔离级别
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
// 解析@Transactionl上的事务超时事件
rbta.setTimeout(attributes.getNumber("timeout").intValue());
// 解析readOnly
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 解析@Transactionl上的事务管理器的名称
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;
}

至此,@Transactional是如何变成RuleBasedTransactionAttribute已经很清晰了。

如何自动生成代理对象

这部分和之前的声明式AOP的源码分析是一样的过程,通过类图过一下。

  1. 谁负责创建代理?

    InfrastructureAdvisorAutoProxyCreator继承我们熟悉的AbstractAdvisorAutoProxyCreator类,是个BeanPostProcessor,在Spring容器启动的过程中,会拦截bean的创建过程,为需要事务支持的bean生成代理对象。

  2. 谁负责判断bean是否需要代理?

    BeanFactoryTransactionAttributeSourceAdvisor是个Advisor,组合了切点和通知。哪些bean需要代理满足增强由切点TransactionAttributeSourcePointcut来通过TransactionAttributeSource来判定bean的类或是方法上是否有@Transactional注解。

  3. 谁负责实际的事务增强工作?

    TransactionInterceptor 继承MethodInterceptor是个拦截器,负责拦截代理对象目标方法,在前后增加事务控制的逻辑。这个类下面进行详细分析。

TransactionInterceptor 如何进行事务控制

TransactionInterceptor类

Spring中声明式事务时通过AOP的方式实现的,事务方法的执行最终都会由TransactionInterceptor invoke()拦截增强的。

package org.springframework.transaction.interceptor;
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
// 获取我们的代理对象的class属性
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction...
/**
* 以事务的方式调用目标方法
* 在这埋了一个钩子函数 用来回调目标方法的
*/
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

事务的实现是委托给TransactionAspectSupport父类实现的。

TransactionAspectSupport类

invokeWithinTransaction() 方法

基于环绕通知的实现事务控制,委托给该类上的其他几个模板方法,其实里面主要内容就是编程式的事务控制了。这是个模板方法,主要功能点如下:

  • 如何获取事务管理器对象
  • 通过事务管理器开启事务
  • 执行目标方法
  • 方法异常如何完成事务
  • 正常返回如何完成事务提交
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 获取我们的事务属性源对象
TransactionAttributeSource tas = getTransactionAttributeSource();
// 通过事务属性源对象获取到当前方法的事务属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// @1获取我们配置的事务管理器对象
final TransactionManager tm = determineTransactionManager(txAttr);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 获取连接点的唯一标识 类名+方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); // 声明式事务处理
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// @2创建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); Object retVal;
try {
// 执行被增强方法,调用具体的处理逻辑
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//@3 异常回滚 如何走?可能只需提交,也可能只需回滚,这个取决于事务的配置
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除事务信息,恢复线程私有的老的事务信息
cleanupTransactionInfo(txInfo);
} //成功后提交,会进行资源储量,连接释放,恢复挂起事务等操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
determineTransactionManager() 事务管理器获取

查找和获取的顺序是:

  1. 先看@Transactional中是否通过value或者transactionManager指定了事务管理器
  2. TransactionInterceptor.transactionManagerBeanName是否有值,如果有,将通过这个值查找事务管理器
  3. TransactionInterceptor.transactionManager是否有值,如果有则返回,这个是通过容器TransactionManagementConfigurer接口设置到TransactionInterceptor中的
  4. 如果上面3种都没有,将从Spring容器中查找TransactionManager类型的作为默认事务管理器
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
// txAttr == null || this.beanFactory == null ,返回拦截器中配置的事务管理器
if (txAttr == null || this.beanFactory == null) {
return getTransactionManager();
} //qualifier就是@Transactional注解中通过value或者transactionManager来指定事务管理器的bean名称
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
//从spring容器中查找[beanName:qualifier,type:TransactionManager]的bean
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
//从spring容器中查找[beanName:this.transactionManagerBeanName,type:TransactionManager]的bean
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
//最后通过类型TransactionManager在spring容器中找事务管理器
TransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
createTransactionIfNecessary() 创建并开启事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果没有名称指定则使用方法唯一标识,并使用DelegatingTransactionAttribute封装txAttr
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
} TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// @1获取TransactionStatus事务状态信息
status = tm.getTransaction(txAttr);
}
}
// @2根据指定的属性与status准备一个TransactionInfo,
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

分析如下:

  • @1获取TransactionStatus事务状态信息,也就是是编程式事务的创建和开启。
  • @2TransactionInfo生成

prepareTransactionInfo() 方法创建事务信息并绑定到当前线程

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) { // 创建事务信息
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// The transaction manager will flag an error if an incompatible tx already exists.
// 设置新事务状态
txInfo.newTransactionStatus(status);
} // 事务信息绑定到当前线程
txInfo.bindToThread();
return txInfo;
}

事务信息是有哪些内容?简单过一下内部类TransactionInfo

/**
* 用于保存事务信息的不透明对象。子类必须将其传递回该类的方法,但不能看到其内部
*/
protected static final class TransactionInfo {
/** 事务管理器 */
@Nullable
private final PlatformTransactionManager transactionManager;
/** 事务属性 */
@Nullable
private final TransactionAttribute transactionAttribute;
/** 切点标识名 */
private final String joinpointIdentification;
/** 事务状态 */
@Nullable
private TransactionStatus transactionStatus;
/** 旧的事务信息 */
@Nullable
private TransactionInfo oldTransactionInfo; /**
* 绑定新事务到当前线程,旧的会被保存
*/
private void bindToThread() {
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
} /**
* 恢复线程中旧事务信息
*/
private void restoreThreadLocalStatus() {
transactionInfoHolder.set(this.oldTransactionInfo);
} }
completeTransactionAfterThrowing() 异常后完成事务
/**
* 如果支持回滚的话就进行回滚,否则就处理提交,提交里面如果TransactionStatus.isRollbackOnly()=true的话也会进行回滚处理
*/
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);
}
// @1判断事务是否需要回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 进行回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
// @2通过事务管理器提交事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}
}

异常后如果匹配上我们@Transaction指定的异常类型,在调用事务管理器进行事务回滚,否则通过事务管理器进行提交事务。

commitTransactionAfterReturning 正常完成事务

通过事务管理器进行事务提交。

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
小结

TransactionInterceptor 对事务控制包括开启、提交、回滚等操作,其实都是通过事务管理器进行的,这和编程式事务管理是一样的。

总结

本文进行了Spring中@Transactional声明事务的源码解析,结合了声明式AOP的源码分析和编程式事务管理的源码分析。总结下过程是就是通过BeanPostProcessor拦截bean创建过程自动创建代理对象,通过TransactionInterceptor 环绕通知增强目标方法,在目标方法执行前后增加事务的控制逻辑。

知识分享,转载请注明出处。学无先后,达者为先!

Spring系列28:@Transactional事务源码分析的更多相关文章

  1. [心得体会]spring事务源码分析

    spring事务源码分析 1. 事务的初始化注册(从 @EnableTransactionManagement 开始) @Import(TransactionManagementConfigurati ...

  2. 【原创】002 | 搭上SpringBoot事务源码分析专车

    前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍** 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为 ...

  3. Spring Environment(二)源码分析

    Spring Environment(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...

  4. 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  5. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  6. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  7. Spring事务源码分析专题(一)JdbcTemplate使用及源码分析

    Spring中的数据访问,JdbcTemplate使用及源码分析 前言 本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成 对源码分析前,我希望先介绍一下Spring中数据访问的相关内容 ...

  8. spring事务源码分析结合mybatis源码(一)

    最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...

  9. Spring Security(四) —— 核心过滤器源码分析

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...

随机推荐

  1. 关于WinForm布局那些事情

    最近项目中,需要用WinForm做一些简单的功能,给第三方作为测试用.本来想着简单的拖几个控件,布局一下就了事了的.但是因为第三方是个大客户,需要展示出我们的技术水平.遂好好的研究了一下WinForm ...

  2. $_SERVER["QUERY_STRING"],$_SERVER["REQUEST_URI"],$_SERVER["SCRIPT_NAME"] 和$_SERVER["PHP_SELF"]

    $_SERVER["QUERY_STRING"],$_SERVER["REQUEST_URI"],$_SERVER["SCRIPT_NAME" ...

  3. 哈工大 信息安全 实验 Snort与单台防火墙联动实验

    XX大学XX学院 <网络攻击与防御> 实验报告 实验报告撰写要求 实验操作是教学过程中理论联系实际的重要环节,而实验报告的撰写又是知识系统化的吸收和升华过程,因此,实验报告应该体现完整性. ...

  4. Word:在文中插入对参考文献的引用

    1.工具栏→插入→交叉引用 2."交叉引用"工具栏 引用类型:编号项: 引用内容:段落编号 选中要引用的参考文献编号 3.结果

  5. k8s全方位监控 -prometheus实现短信告警接口编写(python)

    1.prometheus短信告警接口实现(python)源码如下: import subprocess from flask import Flask from flask import reques ...

  6. CSDN中Markdown编辑器使用方法

    Markdown编辑器 如果想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识. 新的改变 CSDN中Markdown编辑器进行了一些功能拓展与语法支 ...

  7. Nodejs在Linux环境安装

    一.下载安装包 http://nodejs.cn/download/ 二.解压 tar -xf node-v14.3.0-linux-x64.tar.xz 三.配置 1.编辑文件vim /etc/pr ...

  8. 面试官:Redis中有序集合的内部实现方式是什么?

    面试官:Redis中基本的数据类型有哪些? 我:Redis的基本数据类型有:字符串(string).哈希(hash).列表(list).集合(set).有序集合(zset). 面试官:有序集合的内部实 ...

  9. linux访问控制列表 ACL实现文件权限设置

    ACL:Access Control List,实现灵活的文件权限管理 除了文件的所有者,所属组和其它人,可以对更多的用户设置权限 CentOS7 默认创建的xfs和ext4文件系统具有ACL功能 A ...

  10. 聊聊视频中的编解码器,你所不知道的h264、h265、vp8、vp9和av1编解码库

    你知道FFmpeg吗?了解过h264/h265/vp8/vp9编解码库吗? 我们日常生活中使用最广泛的五种视频编码:H264(AVC).H265(HEVC).vp8.vp9.av1都分别是什么?由哪些 ...