Spring 事务处理(上)

简介: 我们通常在项目中使用事务,只需要增加一个 @Transactional 注解就可以正常使用事务定义。我们有去思考过这个注解的含义以及背后的原理吗?本文将从Spring 事务核心,@Transactional 注解工作原理,事务的传播机制等三个方面进行详细阐述。

Spring 事务核心


Spring 事务处理方式分为两种:编程式事务,申明式事务。我们常用的注解的方式也是申明式事务,如果我们通过手动去开启事务,正常完成进行 commit 异常进行 fallback 这种就是编程式事务。我们主要描述申明式事务的实现


启用事务管理


  1. @EnableTransactionManagement 定义如下,在这了加载一个 TransactionManagementConfigurationSelector


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
  // ...
}


  1. TransactionManagementConfigurationSelector 是一个 ImportSelector 加载了两个类 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 。


@Override
protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
        case PROXY:
            //向 Spring 中添加了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 对应的 bean
            return new String[] {AutoProxyRegistrar.class.getName(),
                    ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[] {determineTransactionAspectClass()};
        default:
            return null;
    }
}


3.AutoProxyRegistrar 其实是一个自动代理创建器


- AutoProxyRegistrar#registerBeanDefinitions()
  // 注册自动代理创建器
  -- AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
  // 注册一个 InfrastructureAdvisorAutoProxyCreator 其实它也是实现了 BeanPostProcessor
  -- registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);


  1. ProxyTransactionManagementConfiguration 其实是一个代理逻辑 Advisor 包含了 PoincutAdvice


@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
  @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    //表示切点
    advisor.setTransactionAttributeSource(transactionAttributeSource());
    //表示 advice 代理逻辑
    advisor.setAdvice(transactionInterceptor());
    if (this.enableTx != null) {
      advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
  }
  // TransactionAttributeSource 表示 @Transaction 所包含的信息
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
  }
  // TransactionInterceptor 这里其实是一个代理的逻辑的细节 
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionInterceptor transactionInterceptor() {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    //拦截器中持有的 @Transaction 注解信息
    interceptor.setTransactionAttributeSource(transactionAttributeSource());
    if (this.txManager != null) {
      interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
  }
}
// TransactionInterceptor 类
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    // ...
    @Override
    @Nullable
    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<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        // 执行代理逻辑
        return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }
}
// BeanFactoryTransactionAttributeSourceAdvisor 中定义 
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    // 构造了一个 Pointcut, TransactionAttributeSource 的实现对象为 AnnotationTransactionAttributeSource
    // 在 Pointcut 匹配类时, 会利用 AnnotationTransactionAttributeSource 去检查类上是否有 @Transaction 注解
    // 在 Pointcut 匹配方法时, 会利用 AnnotationTransactionAttributeSource 去检查方法上是否有 @Transaction 注解
    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
        @Override
        @Nullable
        protected TransactionAttributeSource getTransactionAttributeSource() {
            return transactionAttributeSource;
        }
    };
    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }
}
// TransactionAttributeSourcePointcut#matches 进行匹配方法/类上面是否包含 @Transaction 注解
abstract class TransactionAttributeSourcePointcut@ extends StaticMethodMatcherPointcut implements Serializable {
    /**
     * 判断 某个方法或者类 是否存在 @Transaction 注解
     * @param method the candidate method
     * @param targetClass the target class
     * @return
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
                PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
                PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        //某个类或者方法上是否存在 @Transaction 注解
        //调用 AnnotationTransactionAttributeSource 的父类中的 getTransactionAttribute 方法
        //按段是否有 @Transaction 注解, 获取到注解信息不等于 null 则匹配
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }
}    
//tas.getTransactionAttribute(method, targetClass)
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    if (method.getDeclaringClass() == Object.class) {
        return null;
    }
    // First, see if we have a cached value.
    Object cacheKey = getCacheKey(method, targetClass);
    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 {
            return cached;
        }
    }
    else {
        // We need to work it out.
        // 获取 @Transactional 注解信息
        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;
    }
}
//computeTransactionAttribute(method, targetClass);
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    // allowPublicMethodsOnly 默认为 false, 所以在 public 方法上写上 @Transactional 注解没有作用
    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;
        }
    }
    return null;
}


其实到这里可以得到一个结论:


@Transactional 注解对 public 方法才有效,其它的方法是无效的


TransactionInterceptor 对事务方法的执行


  1. TransactionInterceptor invoke 方法是事务执行的核心方法


protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
    final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 获取得到当前方法或者类上的 @Transactional 注解的信息
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 得到一个 @Transactional 注解的信息
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 根据当前执行的类中的某个方法以及 @Transactional 注解的信息生成一个唯一的标示, 这个标示会用来作为事务的名称
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 创建事务,并且得到当前事务的信息,后面需要事务信息来进行提交或者回滚
        // 例如: cn.edu.xxx.tx.service.PersonService.test
        // 这里默认就是 类名全路径 + 方法名
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 执行业务方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 出现异常的清隽管辖,如果是异常需要 rollback , 否则就提交
            completeTransactionAfterThrowing(txInfo, ex);
            // 出现了异常限制性 finally 中的逻辑再抛出异常
            throw ex;
        }
        finally {
            //删除事务
            cleanupTransactionInfo(txInfo);
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        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)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
            });
            // Check result state: It might indicate a Throwable to rethrow.
            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;
        }
    }
}



相关文章
|
11天前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
6月前
|
Java Spring
Spring中事务失效的场景
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是被代理对象调用, 那么@Transactional会失效 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
|
6月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——常见问题总结
本文总结了Spring Boot中使用事务的常见问题,虽然通过`@Transactional`注解可以轻松实现事务管理,但在实际项目中仍有许多潜在坑点。文章详细分析了三个典型问题:1) 异常未被捕获导致事务未回滚,需明确指定`rollbackFor`属性;2) 异常被try-catch“吃掉”,应避免在事务方法中直接处理异常;3) 事务范围与锁范围不一致引发并发问题,建议调整锁策略以覆盖事务范围。这些问题看似简单,但一旦发生,排查难度较大,因此开发时需格外留意。最后,文章提供了课程源代码下载地址,供读者实践参考。
117 0
|
6月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
777 0
|
6月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——事务相关
本文介绍Spring Boot事务配置管理,阐述事务在企业应用开发中的重要性。事务确保数据操作可靠,任一异常均可回滚至初始状态,如转账、购票等场景需全流程执行成功才算完成。同时,事务管理在Spring Boot的service层广泛应用,但根据实际需求也可能存在无需事务的情况,例如独立数据插入操作。
109 0
|
4月前
|
人工智能 Java 数据库连接
Spring事务失效场景
本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
188 10
|
3月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
228 1
|
6月前
|
SQL Java 数据库连接
Spring中的事务是如何实现的
1. Spring事务底层是基于数据库事务和AOP机制的 2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 6. 然后执⾏当前⽅法,⽅法中会执⾏sql 7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务
|
6月前
|
JavaScript Java 开发者
Spring事务失效,常见的情况有哪些?
本文总结了Spring事务失效的7种常见情况,包括未启用事务管理功能、方法非public类型、数据源未配置事务管理器、自身调用问题、异常类型错误、异常被吞以及业务和事务代码不在同一线程中。同时提供了两种快速定位事务相关Bug的方法:通过查看日志(设置为debug模式)或调试代码(在TransactionInterceptor的invoke方法中设置断点)。文章帮助开发者更好地理解和解决Spring事务中的问题。
194 7
|
8月前
|
SQL Java 关系型数据库
【SpringFramework】Spring事务
本文简述Spring中数据库及事务相关衍伸知识点。
98 9