关于java:Spring – @Transactional – 后台会发生什么?

Spring - @Transactional - What happens in background?

我想知道当你用@Transactional注释一个方法时实际会发生什么?当然,我知道Spring将在事务中包装该方法。

但是,我有以下疑问:

  • 我听说Spring创建了一个代理类?有人能更深入地解释一下吗?代理类中实际存在的内容是什么?实际的班级发生了什么?我如何才能看到Spring创建的代理类
  • 我也在Spring文档中读到:
  • Note: Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

    来源:http://static.spring source.org/spring/docs/2.0.x/reference/transaction.html

    为什么只有外部方法调用在事务中,而不是自调用方法?


    这是一个大话题。Spring参考文档为此编写了多个章节。我建议阅读面向方面编程和事务处理方面的知识,因为Spring的声明性事务支持在AOP的基础上使用AOP。

    但是在非常高的层次上,Spring为那些在类本身或成员上声明@transactional的类创建代理。代理在运行时大部分是不可见的。它为Spring提供了一种将方法调用之前、之后或周围的行为注入被代理对象的方法。事务管理只是可以被钩住的行为的一个例子。安全检查是另一回事。您也可以为日志之类的事情提供自己的服务。因此,当用@transactional注释一个方法时,Spring会动态创建一个代理,该代理实现与要注释的类相同的接口。当客户机调用您的对象时,调用会被截获,并通过代理机制注入行为。

    顺便说一下,EJB中的事务工作原理类似。

    正如您所观察到的,通过,代理机制只在从某个外部对象传入调用时工作。当您在对象中进行内部调用时,您实际上是通过"this"引用进行调用,这将绕过代理。然而,有很多方法可以解决这个问题。我在本论坛文章中解释了一种方法,在这种方法中,我使用beanfactoryPostProcessor在运行时将代理的实例注入到"自引用"类中。我将这个引用保存到一个名为"me"的成员变量中。然后,如果我需要进行需要更改线程事务状态的内部调用,那么我将通过代理(例如"me.somemethod()")来引导调用,论坛帖子将对此进行更详细的解释。请注意,BeanFactoryPostProcessor代码现在有点不同,因为它是在Spring1.x时间框架中写回的。但希望它能给你一个想法。我有一个更新的版本,我可能可以提供。


    当Spring加载bean定义并配置为查找@transactional注释时,它将围绕实际bean创建这些代理对象。这些代理对象是运行时自动生成的类的实例。当调用方法时,这些代理对象的默认行为只是在"目标"bean(即您的bean)上调用相同的方法。

    但是,代理也可以与拦截器一起提供,当存在拦截器时,代理将在调用目标bean的方法之前调用这些拦截器。对于用@transactional注释的目标bean,Spring将创建一个TransactionInterceptor,并将其传递给生成的代理对象。因此,当您从客户机代码调用该方法时,您将在代理对象上调用该方法,该对象首先调用TransactionInterceptor(它开始一个事务),然后在目标bean上调用该方法。调用完成后,TransactionInterceptor将提交/回滚该事务。它对客户端代码是透明的。

    至于"外部方法",如果bean调用自己的方法之一,那么它将不会通过代理来这样做。记住,Spring将bean包装在代理中,您的bean不知道这一点。只有来自"外部"bean的调用才能通过代理。

    这有帮助吗?


    作为一个视觉上的人,我喜欢用代理模式的序列图来衡量。如果你不知道怎么读箭头,我读了第一个箭头:Client执行Proxy.method()

  • 客户机从他的角度调用目标上的一个方法,并被代理程序悄悄地截获。
  • 如果定义了before方面,代理将执行它
  • 然后,执行实际方法(目标)
  • 返回后和抛出后是可选的方面在方法返回和/或如果方法引发例外
  • 在这之后,代理执行after方面(如果定义的话)
  • 最后,代理返回到调用客户机
  • Proxy Pattern Sequence Diagram(我被允许张贴照片,条件是我提到了照片的来源。作者:Noel Vaes,网址:www.noel vaes.eu)


    最简单的答案是,在您声明@transactional的任何方法上,事务的边界在方法完成时开始,边界在方法完成时结束。

    如果您使用的是JPA调用,那么所有提交都在这个事务边界中。假设您正在保存entity1、entity2和entity3。现在,在保存Entity3时发生异常,因为Enitiy1和Entity2在同一事务中,所以Entity1和Entity2将使用Entity3回滚。

    事务:(entity1.save,entity2.save,entity3.save)。任何异常都将导致数据库中所有JPA事务的回滚。Spring在内部使用JPA事务。