440

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?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
peakit
  • 28,597
  • 27
  • 63
  • 80
  • 3
    Relevant discussion is here: http://stackoverflow.com/questions/3120143/where-should-i-put-transactional-annotation-at-an-interface-definition-or-at-an/3120323#3120323 – dma_k Sep 26 '10 at 20:07

6 Answers6

318

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.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Rob H
  • 14,502
  • 8
  • 42
  • 45
  • 8
    >> The proxy is mostly invisible at runtime Oh !! I am curious to see them :) Rest.. your answer was very comprehensive. This is the second time you are helping me..Thanks for all the help. – peakit Jul 08 '09 at 17:37
  • 21
    No problem. You can see the proxy code if you step through with a debugger. That's probably the easiest way. There's no magic; they're just classes within the Spring packages. – Rob H Jul 08 '09 at 17:47
  • And if the method which has the @Transaction annotation is implementing an interface the spring will use the dynamic proxy API to inject the transactionalisation and _not_ use proxies. I prefer to have my transactionalised classes implement interfaces in any case. – Michael Wiles Jul 10 '09 at 14:12
  • 1
    I found the “me” scheme too (using explicit wiring to do it as it suits the way I think), but I think that if you're doing it that way, you're probably better off refactoring so that you don't have to. But yes, that might sometimes be very awkward! – Donal Fellows Feb 02 '13 at 23:31
  • does `@Transactional` also close a session opened with `sessionFactory.getCurrentSession()` – kapad Jul 07 '15 at 14:45
  • thank you! this explains why proxies are needed and also why they won't work when invoking something transactional from within the same class – asgs Jul 13 '18 at 08:19
  • If a method has `@Transactional` and `@PreAuthorize` (for security checks) annotations, Does spring create nested proxies or it create a single proxy with all cross-cutting logic? – Carlos Casallas Aug 10 '18 at 16:04
  • 4
    **2019:** As this answer is getting old, the referred forum post is no longer available which would describe the case when _you have to make an internal call within the object **without** bypassing the proxy, using `BeanFactoryPostProcessor`_ . However, there is an (in my opinion) very similar method described in this answer: https://stackoverflow.com/a/11277899/3667003 ...and further solutions in the whole thread as well. – Oliver Apr 03 '19 at 22:31
242

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?

CodeSlave
  • 425
  • 6
  • 21
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • 54
    >Remember, Spring wraps your bean in the proxy, your bean has no knowledge of it **This said it all. What a great answer. Thanks for helping.** – peakit Jul 08 '09 at 17:39
  • 2
    Great explanation, for the proxy and interceptors. Now i understand spring implement a proxy object to intercept calls to a target bean. Thank you! – dharag Aug 20 '13 at 15:39
  • 2
    I think you are trying to describe this picture of the Spring documentation and seeing this picture helps me a lot: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/images/tx.png – WesternGun Nov 22 '18 at 10:33
  • 1
    quite late to the party - `These proxy objects are instances of classes that are auto-generated at runtime.` when does this happen exactly. When the application is loaded in to JVM or when the bean (which should be wrapped by proxy) is called for the first time. – samshers Oct 21 '21 at 09:40
  • This is a very good explanation about how Spring @Transactional annotation works!!! – Chris Lee May 19 '22 at 08:29
72

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)

Lii
  • 11,553
  • 8
  • 64
  • 88
progonkpa
  • 3,590
  • 9
  • 31
  • 50
69

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.

CodeSlave
  • 425
  • 6
  • 21
RoshanKumar Mutha
  • 2,235
  • 1
  • 12
  • 13
  • 13
    "A̶n̶y̶ exception will result in rollback of all JPA transactions with DB." **Note** Only RuntimeException result in rollback. Checked exceptions, won't result in rollback. – Arjun Sunil Kumar Oct 06 '19 at 14:39
  • https://dzone.com/articles/spring-transactional-amp-exceptions#:~:text=%40Transactional%20only%20rolls%20back%20transactions,database%2C%20making%20the%20system%20inconsistent. – Bhaskar13 Apr 08 '21 at 07:51
17

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.

Marco Behler
  • 3,627
  • 2
  • 17
  • 19
8

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.

Danyal Sandeelo
  • 12,196
  • 10
  • 47
  • 78