Spring - @Transactional - 后台发生了什么?

发布于 2024-07-26 21:58:39 字数 739 浏览 6 评论 0原文

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

但是,我有以下疑问:

  1. 我听说Spring创建了一个代理类? 有人可以更深入解释一下吗? 该代理类中实际存在什么? 实际课堂上会发生什么? 我怎样才能看到 Spring 创建的代理类
  2. 我还在 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:

  1. 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
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

拿命拼未来 2024-08-02 21:58:40

当 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 a TransactionInterceptor, 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 the TransactionInterceptor (which begins a transaction), which in turn invokes the method on your target bean. When the invocation finishes, the TransactionInterceptor 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?

¢蛋碎的人ぎ生 2024-08-02 21:58:40

作为一个视觉人员,我喜欢用代理模式的序列图来权衡。 如果您不知道如何阅读箭头,我会这样阅读第一个箭头:Client 执行Proxy.method()

  1. 客户端从他的角度调用目标上的方法,并被代理默默拦截
  2. 如果定义了 before 方面,代理将执行它
  3. 然后,执行实际方法(目标)
  4. After-returning 和 after- throwing 是可选的方面是
    在方法返回后和/或方法抛出异常后执行
    异常
  5. 之后,代理执行 after 方面(如果已定义)
  6. 最后代理返回到调用客户端

代理模式序列图
(我被允许发布这张照片,条件是我提到了它的起源。作者: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 executes Proxy.method().

  1. The client calls a method on the target from his perspective, and is silently intercepted by the proxy
  2. If a before aspect is defined, the proxy will execute it
  3. Then, the actual method (target) is executed
  4. After-returning and after-throwing are optional aspects that are
    executed after the method returns and/or if the method throws an
    exception
  5. After that, the proxy executes the after aspect (if defined)
  6. Finally the proxy returns to the calling client

Proxy Pattern Sequence Diagram
(I was allowed to post the photo on condition that I mentioned its origins. Author: Noel Vaes, website: https://www.noelvaes.eu)

寻梦旅人 2024-08-02 21:58:40

最简单的答案是:

无论在哪种方法上声明@Transactional,事务的边界都会在方法完成时开始和结束。

如果您使用 JPA 调用,则所有提交都在此事务边界内

假设您正在保存实体 1、实体 2 和实体 3。 现在,在保存实体3时发生异常,然后由于enitiy1和entity2进入同一事务,因此entity1和entity2将与entity3一起回滚

交易:

  1. 实体1.保存
  2. entity2.save
  3. entity3.save

使用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 :

  1. entity1.save
  2. entity2.save
  3. entity3.save

Any exception will result in rollback of all JPA transactions with DB.Internally JPA transaction are used by Spring.

梦萦几度 2024-08-02 21:58:40

所有现有的答案都是正确的,但我觉得不能公正地对待这个复杂的话题。

要获得全面、实用的解释,您可能需要查看此 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.

ˉ厌 2024-08-02 21:58:40

可能已经晚了,但我发现了一些东西,它很好地解释了您与代理相关的问题(只有通过代理传入的“外部”方法调用才会被拦截)。

例如,您有一个如下所示的类

@Component("mySubordinate")
public class CoreBusinessSubordinate {
    
    public void doSomethingBig() {
        System.out.println("I did something small");
    }
    
    public void doSomethingSmall(int x){
        System.out.println("I also do something small but with an int");    
  }
}

,并且有一个如下所示的方面:

@Component
@Aspect
public class CrossCuttingConcern {
    
    @Before("execution(* com.intertech.CoreBusinessSubordinate.*(..))")
    public void doCrossCutStuff(){
        System.out.println("Doing the cross cutting concern now");
    }
}

当您像这样执行它时:

 @Service
public class CoreBusinessKickOff {
    
    @Autowired
    CoreBusinessSubordinate subordinate;
 
    // getter/setters
    
    public void kickOff() {
       System.out.println("I do something big");
       subordinate.doSomethingBig();
       subordinate.doSomethingSmall(4);
   }

}

在上面给定代码之上调用 kickOff 的结果。

I do something big
Doing the cross cutting concern now
I did something small
Doing the cross cutting concern now
I also do something small but with an int

但是当您更改时 你看

@Component("mySubordinate")
public class CoreBusinessSubordinate {
    
    public void doSomethingBig() {
        System.out.println("I did something small");
        doSomethingSmall(4);
    }
    
    public void doSomethingSmall(int x){
       System.out.println("I also do something small but with an int");    
   }
}


public void kickOff() {
  System.out.println("I do something big");
   subordinate.doSomethingBig();
   //subordinate.doSomethingSmall(4);
}

,该方法内部调用另一个方法,因此它不会被拦截,并且输出将如下所示:

I do something big
Doing the cross cutting concern now
I did something small
I also do something small but with an int

您可以通过执行以下操作

public void doSomethingBig() {
    System.out.println("I did something small");
    //doSomethingSmall(4);
    ((CoreBusinessSubordinate) AopContext.currentProxy()).doSomethingSmall(4);
}

来绕过此代码片段:
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

@Component("mySubordinate")
public class CoreBusinessSubordinate {
    
    public void doSomethingBig() {
        System.out.println("I did something small");
    }
    
    public void doSomethingSmall(int x){
        System.out.println("I also do something small but with an int");    
  }
}

and you have an aspect, that looks like this:

@Component
@Aspect
public class CrossCuttingConcern {
    
    @Before("execution(* com.intertech.CoreBusinessSubordinate.*(..))")
    public void doCrossCutStuff(){
        System.out.println("Doing the cross cutting concern now");
    }
}

When you execute it like this:

 @Service
public class CoreBusinessKickOff {
    
    @Autowired
    CoreBusinessSubordinate subordinate;
 
    // getter/setters
    
    public void kickOff() {
       System.out.println("I do something big");
       subordinate.doSomethingBig();
       subordinate.doSomethingSmall(4);
   }

}

Results of calling kickOff above given code above.

I do something big
Doing the cross cutting concern now
I did something small
Doing the cross cutting concern now
I also do something small but with an int

but when you change your code to

@Component("mySubordinate")
public class CoreBusinessSubordinate {
    
    public void doSomethingBig() {
        System.out.println("I did something small");
        doSomethingSmall(4);
    }
    
    public void doSomethingSmall(int x){
       System.out.println("I also do something small but with an int");    
   }
}


public void kickOff() {
  System.out.println("I do something big");
   subordinate.doSomethingBig();
   //subordinate.doSomethingSmall(4);
}

You see, the method internally calls another method so it won't be intercepted and the output would look like this:

I do something big
Doing the cross cutting concern now
I did something small
I also do something small but with an int

You can by-pass this by doing that

public void doSomethingBig() {
    System.out.println("I did something small");
    //doSomethingSmall(4);
    ((CoreBusinessSubordinate) AopContext.currentProxy()).doSomethingSmall(4);
}

Code snippets taken from:
https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
The page doesn't exist anymore.

情丝乱 2024-08-02 21:58:39

这是一个很大的话题。 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 a BeanFactoryPostProcessor to inject an instance of the proxy into "self-referencing" classes at runtime. I save this reference to a member variable called me. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文