Spring - @Transactional - 后台发生了什么?
我想知道当您使用 @Transactional
注解一个方法时实际会发生什么? 当然,我知道 Spring 会将该方法包装在 Transaction 中。
但是,我有以下疑问:
- 我听说Spring创建了一个代理类? 有人可以更深入解释一下吗? 该代理类中实际存在什么? 实际课堂上会发生什么? 我怎样才能看到 Spring 创建的代理类
- 我还在 Spring 文档中读到:
注意:由于此机制基于代理,因此只有通过代理传入的“外部”方法调用才会被拦截。 这意味着“自调用”,即目标对象中的方法调用目标对象的其他方法,即使调用的方法被标记为@Transactional,也不会在运行时导致实际事务。代码>!
来源:http://static. springsource.org/spring/docs/2.0.x/reference/transaction.html
为什么事务下只有外部方法调用而不是自调用方法?
I want to know what actually happens when you annotate a method with @Transactional
?
Of course, I know that Spring will wrap that method in a Transaction.
But, I have the following doubts:
- I heard that Spring creates a proxy class? Can someone explain this in more depth. What actually resides in that proxy class? What happens to the actual class? And how can I see Spring's created proxied class
- I also read in Spring docs that:
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
!
Source: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Why only external method calls will be under Transaction and not the self-invocation methods?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
当 Spring 加载您的 bean 定义并配置为查找
@Transactional
注释时,它将围绕您的实际 bean 创建这些代理对象。 这些代理对象是在运行时自动生成的类的实例。 当调用方法时,这些代理对象的默认行为只是在“目标”bean(即您的 bean)上调用相同的方法。然而,代理也可以提供拦截器,当存在这些拦截器时,代理将在调用目标 bean 的方法之前调用这些拦截器。 对于使用
@Transactional
注释的目标bean,Spring将创建一个TransactionInterceptor
,并将其传递给生成的代理对象。 因此,当您从客户端代码调用该方法时,您正在调用代理对象上的方法,该方法首先调用TransactionInterceptor
(它开始事务),后者又调用目标 bean 上的方法。 当调用完成时,TransactionInterceptor 提交/回滚事务。 它对客户端代码是透明的。至于“外部方法”,如果您的 bean 调用它自己的方法之一,那么它不会通过代理来执行此操作。 请记住,Spring 将您的 bean 包装在代理中,您的 bean 对此一无所知。 只有来自 Bean“外部”的调用才会通过代理。
这有帮助吗?
When Spring loads your bean definitions, and has been configured to look for
@Transactional
annotations, it will create these proxy objects around your actual bean. These proxy objects are instances of classes that are auto-generated at runtime. The default behaviour of these proxy objects when a method is invoked is just to invoke the same method on the "target" bean (i.e. your bean).However, the proxies can also be supplied with interceptors, and when present these interceptors will be invoked by the proxy before it invokes your target bean's method. For target beans annotated with
@Transactional
, Spring will create aTransactionInterceptor
, and pass it to the generated proxy object. So when you call the method from client code, you're calling the method on the proxy object, which first invokes theTransactionInterceptor
(which begins a transaction), which in turn invokes the method on your target bean. When the invocation finishes, theTransactionInterceptor
commits/rolls back the transaction. It's transparent to the client code.As for the "external method" thing, if your bean invokes one of its own methods, then it will not be doing so via the proxy. Remember, Spring wraps your bean in the proxy, your bean has no knowledge of it. Only calls from "outside" your bean go through the proxy.
Does that help?
作为一个视觉人员,我喜欢用代理模式的序列图来权衡。 如果您不知道如何阅读箭头,我会这样阅读第一个箭头:
Client
执行Proxy.method()
。在方法返回后和/或方法抛出异常后执行
异常
(我被允许发布这张照片,条件是我提到了它的起源。作者:Noel Vaes,网站:https://www. noelvaes.eu)
As a visual person, I like to weigh in with a sequence diagram of the proxy pattern. If you don't know how to read the arrows, I read the first one like this:
Client
executesProxy.method()
.executed after the method returns and/or if the method throws an
exception
(I was allowed to post the photo on condition that I mentioned its origins. Author: Noel Vaes, website: https://www.noelvaes.eu)
最简单的答案是:
无论在哪种方法上声明
@Transactional
,事务的边界都会在方法完成时开始和结束。如果您使用 JPA 调用,则所有提交都在此事务边界内。
假设您正在保存实体 1、实体 2 和实体 3。 现在,在保存实体3时发生异常,然后由于enitiy1和entity2进入同一事务,因此entity1和entity2将与entity3一起回滚。
交易:
使用JPA事务。
The simplest answer is:
On whichever method you declare
@Transactional
the boundary of transaction starts and boundary ends when method completes.If you are using JPA call then all commits are with in this transaction boundary.
Lets say you are saving entity1, entity2 and entity3. Now while saving entity3 an exception occur, then as enitiy1 and entity2 comes in same transaction so entity1 and entity2 will be rollback with entity3.
Transaction :
Any exception will result in rollback of all JPA transactions with DB.Internally JPA transaction are used by Spring.
所有现有的答案都是正确的,但我觉得不能公正地对待这个复杂的话题。
要获得全面、实用的解释,您可能需要查看此 Spring @Transactional In-Depth 指南,尽力用大约 4000 个简单单词涵盖事务管理,并带有大量代码示例。
All existing answers are correct, but I feel cannot give this complex topic justice.
For a comprehensive, practical explanation you might want to have a look at this Spring @Transactional In-Depth guide, which tries its best to cover transaction management in ~4000 simple words, with a lot of code examples.
可能已经晚了,但我发现了一些东西,它很好地解释了您与代理相关的问题(只有通过代理传入的“外部”方法调用才会被拦截)。
例如,您有一个如下所示的类
,并且有一个如下所示的方面:
当您像这样执行它时:
}
在上面给定代码之上调用 kickOff 的结果。
但是当您更改时 你看
,该方法内部调用另一个方法,因此它不会被拦截,并且输出将如下所示:
您可以通过执行以下操作
来绕过此代码片段:
https://www.intertech.com/Blog/secrets -of-the-spring-aop-proxy/
该页面已不存在。
It may be late but I came across something which explains your concern related to proxy (only 'external' method calls coming in through the proxy will be intercepted) nicely.
For example, you have a class that looks like this
and you have an aspect, that looks like this:
When you execute it like this:
}
Results of calling kickOff above given code above.
but when you change your code to
You see, the method internally calls another method so it won't be intercepted and the output would look like this:
You can by-pass this by doing that
Code snippets taken from:
https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
The page doesn't exist anymore.
这是一个很大的话题。 Spring 参考文档用了多个章节来介绍它。 我建议阅读 面向方面的内容编程和事务,因为 Spring 的声明式事务支持在其基础上使用了 AOP。
但在非常高的层面上,Spring 为在类本身或成员上声明 @Transactional 的类创建代理。 代理在运行时大部分是不可见的。 它为 Spring 提供了一种在方法调用之前、之后或周围将行为注入到被代理的对象中的方法。 事务管理只是可挂钩行为的一个示例。安全检查是另一个示例。 您也可以提供自己的日志记录等功能。 因此,当您使用 @Transactional 注释方法时,Spring 会动态创建一个代理,该代理实现与您注释的类相同的接口。 当客户端调用您的对象时,调用将被拦截并通过代理机制注入行为。
顺便说一句,EJB 中的事务工作方式类似。
正如您所观察到的,代理机制仅在来自某个外部对象的调用时才起作用。 当您在对象内进行内部调用时,您实际上是通过
this
引用进行调用,这会绕过代理。 然而,有一些方法可以解决这个问题。 我在 此论坛帖子 中解释了一种方法,其中我使用 BeanFactoryPostProcessor 在运行时将代理实例注入“自引用”类中。 我将此引用保存到名为me
的成员变量中。 然后,如果我需要进行需要更改线程事务状态的内部调用,我可以通过代理直接调用(例如me.someMethod()
。)论坛帖子有更详细的解释。请注意,BeanFactoryPostProcessor 代码现在会有些不同,因为它是在 Spring 1.x 时间范围内写回的。 但希望它能给你一个想法。 我有一个可能可以提供的更新版本。
This is a big topic. The Spring reference doc devotes multiple chapters to it. I recommend reading the ones on Aspect-Oriented Programming and Transactions, as Spring's declarative transaction support uses AOP at its foundation.
But at a very high level, Spring creates proxies for classes that declare
@Transactional
on the class itself or on members. The proxy is mostly invisible at runtime. It provides a way for Spring to inject behaviors before, after, or around method calls into the object being proxied. Transaction management is just one example of the behaviors that can be hooked in. Security checks are another. And you can provide your own, too, for things like logging. So when you annotate a method with@Transactional
, Spring dynamically creates a proxy that implements the same interface(s) as the class you're annotating. And when clients make calls into your object, the calls are intercepted and the behaviors injected via the proxy mechanism.Transactions in EJB work similarly, by the way.
As you observed, through, the proxy mechanism only works when calls come in from some external object. When you make an internal call within the object, you're really making a call through the
this
reference, which bypasses the proxy. There are ways of working around that problem, however. I explain one approach in this forum post in which I use aBeanFactoryPostProcessor
to inject an instance of the proxy into "self-referencing" classes at runtime. I save this reference to a member variable calledme
. Then if I need to make internal calls that require a change in the transaction status of the thread, I direct the call through the proxy (e.g.me.someMethod()
.) The forum post explains in more detail.Note that the
BeanFactoryPostProcessor
code would be a little different now, as it was written back in the Spring 1.x timeframe. But hopefully it gives you an idea. I have an updated version that I could probably make available.