Hi,

After my post Spring: Transaction propagation / readOnly on the propagation and readonly attribute, I would write some words concerning the propagation and the visibility of annoted method because I was victim to not having read up on how Spring proxies work, the ones responsible for annotation-based transaction support.

So I will expose some problem via examples of how Spring proxies work, the propagation REQUIRED and REQUIRES_NEW and the visibility of annotated methods, then, the possible ways to resolve the encountered problem.

  1. PROBLEM
    • Example n°1
      We have the class MyClass:

      @Transactional(propagation = Propagation.REQUIRED) 
      public class MyClass implementes MyInterface { ... 
      
      	@Override
      	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
      	public void myMethod1(String[] params){
      		//...
      		myMethod2(params);
      		//...
      	}
      //...
      }
      

      … with the interface MyInterface:

      public MyInterface {
      	public void myMethod1(String[] params);
      }
      

      Now let’s say in myMethod1 we call a private method myMethod2 in MyClass:

      	@Override
      	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
      	private void myMethod2(String[] params){
      		//...
      	}
      

      When, we execute the method myMethod1, it seems that Spring “ignores” the REQUIRES_NEW annotation and doesn’t start a new transaction for the second method myMethod2.
      From the Spring reference 2.5:
      When using proxies, the @Transactional annotation should only be applied to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error will be raised, but the annotateRTd method will not exhibit the configured transactional settings.

      So Spring ignores @Transactional annotation on non-public methods and Spring AOP operates on the interface level (MyInterface) and doesn’t intercept any calls to MyClass methods.

    • Example n°2
      We will modify the visibility of the method myMethod2 from public to public:

      	@Override
      	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
      	public void myMethod2(String[] params){
      		//...
      	}
      

      with the interface MyInterface:

      public MyInterface {
      	public void myMethod1(String[] params);
      	public void myMethod2(String[] params);
      }
      

      When, we execute the method myMethod1, it seems that Spring “ignores” again the REQUIRES_NEW annotation and doesn’t start a new transaction for the second method myMethod2.
      From the Spring reference 2.5:
      In proxy mode (which is the default), 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!

      Even if we modify the second method myMethod2 to public, calling it from within the method myMethod1 of same class will not start a new transaction.

  2. SOLUTIONS
    So, Spring turns out the second method myMethod2 REQUIRES_NEW annotation and ignores when the method is called from within the same class. How
    Are there any way to start a new transaction within the first method myMethod1 transaction?
    Is the only way to call another Spring managed bean that has transactions configured as REQUIRES_NEW?

    • Solution n°1:
      The first complex solution is to use aspectj mode in transaction settings so that the transaction related code is weaved in the class and no proxy is created at runtime:

      <tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>
      

      From the Spring reference 2.5:
      Consider the use of AspectJ mode, if you expect self-invocations to be wrapped with transactions as well. In this case, there won’t be a proxy in the first place; instead, the target class will be ‘weaved’ (i.e. its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.

      The default mode “proxy” will process annotated beans to be proxied using Spring’s AOP framework (following proxy semantics, as discussed above, applying to method calls coming in through the proxy only). The alternative mode “aspectj” will instead weave the affected classes with Spring’s AspectJ transaction aspect (modifying the target class byte code in order to apply to any kind of method call). AspectJ weaving requires spring-aspects.jar on the classpath as well as load-time weaving (or compile-time weaving) enabled.

    • Solution n°2:
      As Spring creates a proxy class for the class which has annotated methods, a simple solution is to move the second method myMethod2() to a separate class and the problem disappeared.

      More, any annotation on the called method would be ignored (since the call happens on ‘this’ rather than on the Proxy) when one method in the proxied class calls another method in the same proxied class, Spring is not able to handle this scenario implicitly.

    • Solution n°3:
      So, as any annotation on the called method would be ignored when one method in the proxied class calls another method in the same proxied class, another possible way of doing this is fetching the Spring proxy of the class, in the class itself and call methods on it rather than this:

      @Transactional(propagation = Propagation.REQUIRED) 
      public class MyClass implementes MyInterface { ... 
      
      	@Autowired     
      	private ApplicationContext applicationContext; 
      
      	private MyInterface getSpringProxy() {
      		return applicationContext.getBean(this.getClass());    
      	} 
      
      	@Override
      	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
      	public void myMethod1(String[] params){
      		//...
      		MyInterface myself = getSpringProxy(); 
      		//...
      		myself.myMethod2(params);
      		//...
      	}
      //...
      }
      

    • Solution n°4:
      Another possible way of doing this it the programmatically start a new transaction using TransactionTemplate:

      @Transactional(propagation = Propagation.REQUIRED) 
      public class MyClass implementes MyInterface { ... 
      
      	@Autowired     
      	private ApplicationContext applicationContext; 
      
      	@Override
      	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
      	public void myMethod1(final String[] params){
      		//...
      		PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager");
      		TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
      		transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
      		//
      		transactionTemplate.execute(new TransactionCallback() {
      			public Object doInTransaction(TransactionStatus status) {         
      				// ...
      				myMethod2(params);
      				// ...
      			} 
      		});
      	}
      //...
      }
      

      Note: The method doInTransaction can accessed only to final (constant) objects/variables.

In this post, we have exposed some problem via examples of how Spring proxies work, the propagation REQUIRED and REQUIRES_NEW and the visibility of annotated methods, then, the possible ways to resolve the encountered problem.

Source:

That’s all!!!!

Best regards,

Huseyin OZVEREN