掘金 后端 ( ) • 2024-03-22 11:05

嗨,大家好,我是小米!今天我要和大家聊一聊一个在阿里巴巴面试中常被问到的话题: @Transactional注解。作为一个喜欢分享技术的小伙伴,我希望通过这篇文章,能够帮助大家更好地理解和应用这个关键的技术点。

@EnableTransactionManagement

在开始讨论 @Transactional之前,首先我们来看看 @EnableTransactionManagement注解。这个注解在Spring框架中扮演着关键的角色,它是启用注解驱动的事务管理功能的入口点。要深入了解这个注解,我们需要仔细研究其源代码,并理解它的实现原理。

首先,我们来看看 @EnableTransactionManagement注解的定义。在Spring源代码中,它实际上是一个组合注解,包含了 @Import@Transactional注解。 @Import注解的作用是将指定的配置类导入到当前配置类中,而 @Transactional注解则表示启用基于注解的事务管理。

@EnableTransactionManagement注解的实现中,最重要的部分是TransactionManagementConfigurationSelector类。这个类实现了ImportSelector接口,负责根据注解的属性配置来选择合适的配置类导入到Spring容器中。

selectImports方法中,TransactionManagementConfigurationSelector根据注解的属性配置,选择合适的配置类导入。其中,如果我们没有显式地指定事务管理器的类型,它会根据当前环境中的Bean定义来自动选择一个合适的事务管理器。

另外, @EnableTransactionManagement注解还支持配置mode属性,用来指定事务的模式。默认情况下,它是AdviceMode.PROXY,表示使用代理模式来管理事务。除此之外,还可以配置成AdviceMode.ASPECTJ,表示使用AspectJ切面来管理事务。

@Transactional

@Transactional注解在Spring中扮演着关键的角色,它负责管理事务的生命周期,确保数据操作的一致性和完整性。让我们来揭开这个注解背后的神秘面纱,从源代码的角度来探索其工作原理。

首先,我们来看看 @Transactional注解的定义。在Spring源代码中,它实际上是一个元注解,即一个用于修饰其他注解的注解。通过 @Transactional注解,我们可以将事务管理应用到方法或类级别。

@Transactional注解的实现中,最核心的部分是其对应的事务增强器(TransactionInterceptor)。事务增强器是一个拦截器,它负责在方法调用前后添加事务管理逻辑。当一个被 @Transactional注解修饰的方法被调用时,事务增强器会根据注解的属性配置来决定事务的行为,例如事务的传播属性、隔离级别、超时时间等。

invoke方法中,事务增强器会在方法调用前后分别执行TransactionAspectSupport中定义的invokeWithinTransaction方法,来实现事务的开启、提交或回滚等操作。通过这种方式, @Transactional注解能够确保被修饰方法的数据操作在事务的保护下进行,保证数据的一致性和完整性。

除了事务增强器, @Transactional注解的实现还涉及到事务管理器(PlatformTransactionManager)等核心组件。事务管理器负责管理事务的生命周期,包括事务的开启、提交、回滚等操作。

事务传播属性

在使用 @Transactional注解时,我们经常需要指定事务的传播属性。事务传播属性定义了事务在不同调用之间如何传播。下面是几种常见的事务传播属性:

  • REQUIRED: 表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的传播属性,也是最常用的一种。当我们需要确保某个方法在事务的保护下执行,并且希望它能够参与到已存在的事务中时,就可以选择使用REQUIRED传播属性。
  • MANDATORY: 表示当前方法必须在一个已存在的事务中执行,否则将抛出异常。这种传播属性通常用于要求某些方法必须在事务的上下文中被调用,例如在一些事务管理的框架或库中,可能会要求一些方法必须在事务的控制下执行。
  • NEVER: 表示当前方法不能在事务中执行,如果当前存在事务,则抛出异常。这种传播属性通常用于要求某些方法不应该在事务的上下文中执行,例如在某些特定的业务场景下,我们可能希望某些操作不受事务的影响。
  • NOT_SUPPORTED: 表示当前方法不应在事务中执行,如果当前存在事务,则将其挂起。这种传播属性通常用于要求某些方法在不受事务管理的情况下执行,例如在一些与事务无关的操作中,我们可以选择使用NOT_SUPPORTED传播属性。
  • REQUIRES_NEW: 表示当前方法应该创建一个新的事务,即使已经存在事务也应该将其挂起。这种传播属性通常用于要求某些方法在一个新的事务中执行,而不受外部事务的影响。
  • SUPPORTS: 表示当前方法支持当前事务,如果当前没有事务,则以非事务方式执行。这种传播属性通常用于要求某些方法可以选择参与到已存在的事务中,但如果没有事务也可以独立执行。
  • NESTED: 表示当前方法应该在一个嵌套的事务中执行;如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED类似的操作。这种传播属性通常用于要求某些方法在一个嵌套的事务中执行,而不受外部事务的影响。

如何选择合适的传播属性?

在实际开发中,我们需要根据具体的业务需求和场景来选择合适的事务传播属性。例如,如果我们希望某个方法参与到已存在的事务中,可以选择使用REQUIRED传播属性;如果希望某个方法必须在事务的上下文中执行,可以选择使用MANDATORY传播属性。

总的来说,正确选择事务传播属性对于确保数据操作的一致性和完整性非常重要,需要充分考虑各种因素并结合实际情况做出合适的决策。

注意事项

在使用 @Transactional注解时,有一些重要的注意事项需要我们特别关注,以避免出现意外情况或性能问题。以下是一些常见的注意事项:

  • 事务函数中不要处理耗时任务: 在事务函数中处理耗时任务可能会导致长期占有数据库连接,从而影响系统的性能表现。因此,应该尽量避免在事务函数中处理耗时任务,特别是在高并发环境下,这可能导致数据库连接池资源被耗尽,影响系统的稳定性。
  • 事务函数中不要处理无关业务: 在事务函数中处理无关业务可能会增加出现异常的风险,导致事务回滚。事务回滚会影响其他正常业务的执行,因此应该尽量避免在事务函数中处理与事务无关的业务。如果确实需要处理其他业务,应该考虑将其放在事务之外进行处理,以降低出现异常的影响范围。
  • 合理设置事务的范围和粒度: 在设计事务时,应该根据业务需求和数据操作的复杂性来合理设置事务的范围和粒度。过大的事务范围可能会导致事务持有锁的时间过长,影响并发性能;而过小的事务粒度可能会增加事务管理的开销,降低系统的性能表现。因此,需要根据具体情况权衡利弊,选择合适的事务范围和粒度。
  • 注意事务的隔离级别: 在使用事务管理时,需要特别注意事务的隔离级别。不同的隔离级别会对数据库的并发性能和数据一致性产生不同的影响。因此,在设计事务时,需要根据业务需求和系统性能要求来选择合适的隔离级别,并进行必要的测试和性能调优。

END

总的来说, @Transactional注解是Spring框架中非常重要的一个注解,能够帮助我们管理事务,保证数据的一致性和完整性。但是在使用时,我们需要注意事务的传播属性和一些注意事项,以避免出现意外情况。希望通过今天的分享,能够帮助到大家更好地理解和应用 @Transactional注解。如果你有什么疑问或者想法,欢迎在下方留言,让我们一起讨论交流吧!

最后,祝大家学习进步,工作顺利!

如有疑问或者更多的技术分享,欢迎关注我的微信公众号“知其然亦知其所以然”!