深入理解Spring事务

发布于:2024-12-08 ⋅ 阅读:(102) ⋅ 点赞:(0)

目录


 

什么是Spring事务

        事务其实是一个并发控制单位,是用户定义的一个操作序列,这些操作要么全部完成,要不全部不完成,是一个不可分割的工作单位。事务有 ACID 四个特性,即:

  • 原子性(Atomicity):事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。
  • 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
  • 隔离性(Isolation):多个事务之间是独立的,不相互影响的。
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

而我们说的 Spring 事务,其实是事务在 Spring 中的实现。

为什么需要Spring事务

为了解释清楚这个问题,我们举个简单的例子:银行里树哥要给小黑转 1000 块钱,这时候会有两个必要的操作:

  1. 将树哥的账户余额减少 1000 元。
  2. 将小黑的账户余额增加 1000 元。

这两个操作,要么一起都完成,要么都不完成。如果其中某个成功,另外一个失败,那么就会出现严重的问题。而我们要保证这个操作的原子性,就必须通过 Spring 事务来完成,这就是 Spring 事务存在的原因。

如果你深入了解过 MySQL 事务,那么你应该知道:MySQL 默认情况下,对于所有的单条语句都作为一个单独的事务来执行。我们要使用 MySQL 事务的时候,可以通过手动提交事务来控制事务范围。Spring 事务的本质,其实就是通过 Spring AOP 切面技术,在合适的地方开启事务,接着在合适的地方提交事务或回滚事务,从而实现了业务编程层面的事务操作。

Spring事务的实现

在此之前需要提前讲解一下下面三个接口:

  1. TransactionDefinition :定义一些基本的事务属性
    public interface TransactionDefinition {
        int PROPAGATION_REQUIRED = 0;
        int PROPAGATION_SUPPORTS = 1;
        int PROPAGATION_MANDATORY = 2;
        int PROPAGATION_REQUIRES_NEW = 3;
        int PROPAGATION_NOT_SUPPORTED = 4;
        int PROPAGATION_NEVER = 5;
        int PROPAGATION_NESTED = 6;
        int ISOLATION_DEFAULT = -1;
        int ISOLATION_READ_UNCOMMITTED = 1;
        int ISOLATION_READ_COMMITTED = 2;
        int ISOLATION_REPEATABLE_READ = 4;
        int ISOLATION_SERIALIZABLE = 8;
        int TIMEOUT_DEFAULT = -1;
        // 返回事务的传播行为,默认值为 REQUIRED。
        int getPropagationBehavior();    
        // 返回事务的隔离级别,默认值是 DEFAULT
        int getIsolationLevel();    
        // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
        int getTimeout();    
        // 返回是否为只读事务,默认值为 false
        boolean isReadOnly();
        @Nullable
        String getName();
    }
    
  2. PlatformTransactionManager:事务管理器接口,Spring 为各个平台如:JDBC、MyBatis(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
    public interface PlatformTransactionManager {
        // 获得事务
        TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
        // 提交事务
        void commit(TransactionStatus var1) throws TransactionException;
        // 回滚事务
        void rollback(TransactionStatus var1) throws TransactionException;
    }
    
  3. TransactionStatus:接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息
    public interface TransactionStatus{
        // 是否是新的事务
        boolean isNewTransaction();
        // 是否有恢复点
        boolean hasSavepoint();
        // 设置为只回滚
        void setRollbackOnly();
        // 是否为只回滚
        boolean isRollbackOnly();
        // 是否已完成
        boolean isCompleted;
    }
    

回归正文:Spring事务的实现方式主要有两种:

①编程式事务:这种方式通过编码的方式直接管理事务,通常是使用PlatformTransactionManager接口的实现,通过手动的开启事务、关闭事务以及回滚事务。编程式事务管理提供了最细粒度的控制,但代码复杂性较高,且容易出错。示例代码:

@Service
public class AccountService {

    private  AccountMapper accountmapper;
    //事务管理器
    private  PlatformTransactionManager transactionManager;

    //构造器方式注入
    public AccountService(AccountMapper accountmapper, PlatformTransactionManager transactionManager) {
        this.accountmapper = accountmapper;
        this.transactionManager = transactionManager;
    }

    //转账方法,使用编程式事务管理。
    public void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {
        //TransactionDefinition:定义一些基本的事务属性
        TransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        //通过事务管理器TransactionManager获取到一个事务,TransactionStatus包含了事务的当前状态和一些其他与事务相关的信息
        //在这里开启了事务
        TransactionStatus status = transactionManager.getTransaction(definition);

        try {
            //执行业务逻辑
            Account fromAccount = accountmapper.findById(fromAccountId).orElseThrow();
            Account toAccount = accountmapper.findById(toAccountId).orElseThrow();
            fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
            toAccount.setBalance(toAccount.getBalance().add(amount));
            accountmapper.save(fromAccount);
            accountmapper.save(toAccount);

            //提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            //回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
}

②声明式事务:这是Spring推荐的方式,通过在方法上使用@Transactional注解来管理事务。声明式事务管理使得事务管理变得声明化,代码更加简洁,且减少了出错的可能性。@Transactional注解可以指定传播行为、隔离级别等事务属性,从而控制事务的行为 。示例代码:

@Service
public class AccountService {

    private  AccountMapper accountmapper;

    //构造器方式注入
    public AccountService(AccountMapper accountmapper) {
        this.accountmapper = accountmapper;
    }

    //转账方法,使用声明式式事务管理。
    @Transactional
    public void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {
          //执行业务逻辑
          Account fromAccount = accountmapper.findById(fromAccountId).orElseThrow();
          Account toAccount = accountmapper.findById(toAccountId).orElseThrow();
          fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
          toAccount.setBalance(toAccount.getBalance().add(amount));
          accountmapper.save(fromAccount);
          accountmapper.save(toAccount);
    }
}

 

 Spring事务的传播机制

        事务的传播行为用来描述:系统中的一些方法交由spring来管理事务,当这些方法之间出现嵌套调用的时候,事务所表现出来的行为是什么样的,例如:

         Service1中的m1方法和Service2中的m2方法上面都有@Transactional注解,说明这2个方法由spring来控制事务。但是注意m1中2行代码,先执行了一个insert,然后调用service2中的m2方法,service2中的m2方法也执行了一个insert。

        那么大家觉得这2个insert会在一个事务中运行么?也就是说此时事务的表现行为是什么样的呢?这个就是spring事务的传播行为来控制的事情,不同的传播行为,表现会不一样,可能他们会在一个事务中执行,也可能不会在一个事务中执行,这就需要看传播行为的配置了。

注意:这7种传播行为有个前提,他们的事务管理器是同一个的时候,才会有上面描述中的表现行为!!!!!!!!

推荐去看这篇文章,讲述了事务传播行为之间的各种搭配情况分析

Spring事务的底层原理

         我们知道声明式事务的实现是通过通过aop的功能,通过拦截器拦截 @Transaction 方法,在方法前后添加事务功能。具体步骤:①第一步在启动类上加上@EnableTransactionmanagement注解 ②第二步在目标方法上加上@Transaction注解即可实现 

 @EnableTransactionManagement --开启Spring管理事务

        @EnableTransactionManagement注解会开启spring自动管理事务的功能,有了这个注解之后,spring容器启动的过程中,会拦截所有bean的创建过程,判断bean 是否需要让spring来管理事务,即判断bean中是否有@Transaction注解,如果找到就会被spring容器通过aop的方式创建代理,代理中会添加一个拦截器,通过拦截器在方法前后添加事务的功能

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

 // 是基于类的代理(cglib),还是基于接口的代理(jdk动态代理),默认为false,表示是基于jdk动态代理
 boolean proxyTargetClass() default false;
    
 //通知的模式,默认是通过aop的方式
 AdviceMode mode() default AdviceMode.PROXY;

 // 我们知道这个注解的功能最终是通过aop的方式来实现的,对bean创建了一个代理,代理中添加了一个拦截器
 // 当代理中还有其他拦截器的是时候,可以通过order这个属性来指定事务拦截器的顺序
 // 默认值是 LOWEST_PRECEDENCE = Integer.MAX_VALUE,拦截器的执行顺序是order升序
 int order() default Ordered.LOWEST_PRECEDENCE;
}

@Import(TransactionManagementConfigurationSelector.class) --提供两个bean

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    //这个方法会返回一个类名数组,spring容器启动过程中会自动调用这个方法,
    //将这个方法指定的类注册到spring容器中;方法的参数是AdviceMode,这个就是        
    //@EnableTransactionManagement注解中mode属性的值,默认是PROXY
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:      <-----//默认会走这里
                return new String[]{
                    AutoProxyRegistrar.class.getName(),                     
                    ProxyTransactionManagementConfiguration.class.getName()
                };
            case ASPECTJ:
                return new String[]{this.determineTransactionAspectClass()};
            default:
                return null;
        }
    }
}

最终会在Spring中注册下面两个bean:

AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar --启用AOP的功能,创建一个bean后置处理器,会拦截所有bean的创建,对符合条件的bean创建代理。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    Iterator var5 = annTypes.iterator();

    while(var5.hasNext()) {
        String annType = (String)var5.next();
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (candidate != null) {
            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) {
                    //这个代码的作用就是在容器中做了一个非常关键的bean:InfrastructureAdvisorAutoProxyCreator,
                    //这个类是bean后置处理器,会拦截所有bean的创建,对符合条件的bean创建代理。
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean)proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
    }
}
ProxyTransactionManagementConfiguration --添加事务事务拦截器:TransactionInterceptor
//部分源码
@Configuration(proxyBeanMethods = false)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    @Bean
    @Role(2)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }
}

二者结合起来的效果就是:对@Transaction标注的bean创建代理对象,代理对象中通过TransactionInterceptor拦截器来实现事务管理的功能。

TransactionInterceptor --负责拦截@Transaction方法的执行,在方法执行之前开启spring事务,方法执行完毕之后提交或者回滚事务

//部分源码
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
 
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        Method var10001 = invocation.getMethod();
        invocation.getClass();
        return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(this.getTransactionManagerBeanName());
        oos.writeObject(this.getTransactionManager());
        oos.writeObject(this.getTransactionAttributeSource());
        oos.writeObject(this.getBeanFactory());
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        this.setTransactionManagerBeanName((String)ois.readObject());
        this.setTransactionManager((PlatformTransactionManager)ois.readObject());
        this.setTransactionAttributeSource((TransactionAttributeSource)ois.readObject());
        this.setBeanFactory((BeanFactory)ois.readObject());
    }
}

invokeWithinTransaction --事务拦截器的入口

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
   final InvocationCallback invocation) throws Throwable {

    TransactionAttributeSource tas = getTransactionAttributeSource();
 //@6-1:获取事务属性配置信息:通过TransactionAttributeSource.getTransactionAttribute解析@Trasaction注解得到事务属性配置信息
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    //@6-2:获取事务管理器
    final TransactionManager tm = determineTransactionManager(txAttr);

    //将事务管理器tx转换为 PlatformTransactionManager
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // createTransactionIfNecessary内部,这里就不说了,内部主要就是使用spring事务硬编码的方式开启事务,最终会返回一个TransactionInfo对象
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
  // 业务方法返回值
        Object retVal;
        try {
            //调用aop中的下一个拦截器,最终会调用到业务目标方法,获取到目标方法的返回值
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            //6-3:异常情况下,如何走?可能只需提交,也可能只需回滚,这个取决于事务的配置
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            //清理事务信息
            cleanupTransactionInfo(txInfo);
        }
  //6-4:业务方法返回之后,只需事务提交操作
        commitTransactionAfterReturning(txInfo);
        //返回执行结果
        return retVal;
    }
}

这个方法的执行流程非常像我们编程式事务的实现方式

1、定义事务属性信息:TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
2、定义事务管理器:PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
3、获取事务:TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
4、执行sql操作:比如上面通过JdbcTemplate的各种方法执行各种sql操作
5、提交事务(platformTransactionManager.commit)或者回滚事务(platformTransactionManager.rollback)

determineTransactionManager --获取事务管理器

我们再具体来看:首先是如何通过determineTransactionManager()方法获取事务管理器的:

@Nullable
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    if (txAttr != null && this.beanFactory != null) {
        String qualifier = txAttr.getQualifier();
        if (StringUtils.hasText(qualifier)) {
            return this.determineQualifiedTransactionManager(this.beanFactory, qualifier);
        } else if (StringUtils.hasText(this.transactionManagerBeanName)) {
            return this.determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
        } else {
            TransactionManager defaultTransactionManager = this.getTransactionManager();
            if (defaultTransactionManager == null) {
                defaultTransactionManager = (TransactionManager)this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
                if (defaultTransactionManager == null) {
                    defaultTransactionManager = (TransactionManager)this.beanFactory.getBean(TransactionManager.class);
                    this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
                }
            }
            return defaultTransactionManager;
        }
    } else {
        return this.getTransactionManager();
    }

从上面可知,事务管理器的查找顺序:

1、先看@Transactional中是否通过value或者transactionManager指定了事务管理器

2、TransactionInterceptor.transactionManagerBeanName(即我们在TransactionInterceptor类中通过构造方法传进来的事务管理器)是否有值,如果有,将通过这个值查找事务管理器

3、如果上面2种都没有,将从spring容器中查找TransactionManager类型的事务管理器

completeTransactionAfterThrowing --异常如何处理

然后再来看completeTransactionAfterThrowing()方法中出现异常该如何处理

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        //@6-3-1:判断事务是否需要回滚
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            //通过事务管理器回滚事务
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        }
        else {
            //通过事务管理器提交事务
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }
}

//跟着进入rollbackOn方法
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;
    //@Trasaction中可以通过rollbackFor指定需要回滚的异常列表,通过noRollbackFor属性指定不需要回滚的异常
    //根据@Transactional中指定的回滚规则判断ex类型的异常是否需要回滚
    if (this.rollbackRules != null) {
        for (RollbackRuleAttribute rule : this.rollbackRules) {
            int depth = rule.getDepth(ex);
            if (depth >= 0 && depth < deepest) {
                deepest = depth;
                winner = rule;
            }
        }
    }
    //若@Transactional注解中没有匹配到,这走默认的规则,将通过super.rollbackOn来判断
    if (winner == null) {
        return super.rollbackOn(ex);
    }
    return !(winner instanceof NoRollbackRuleAttribute);
}

//跟着进入rollbackOn方法
@Override
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

根据上面的源码分析,可以看出默认情况下,异常类型是RuntimeException或者Error的情况下,事务才会回滚 @Trasaction中可以通过rollbackFor指定需要回滚的异常列表,通过noRollbackFor属性指定不需要回滚的异常。

源码干货暂时分析到这,已经干的不行了

这里推荐看一篇非常详细且为参考的文章:@Transaction源码深度解析

Spring事务失效的场景 

 

扩展:多数据源的事务管理 

 


网站公告

今日签到

点亮在社区的每一天
去签到