The Spring documentation recommends against putting @Transactional annotations on interface methods because interface annotations are not inherited by classes. However, with Java 8 we can provide a concrete default implementation in the interface. If such a default interface method needs to be the transactional boundary, we have no other choice: we have to put the @Transactional annotation on the interface method.
Will this work (i.e. will spring respect the transaction boundary in this case)? If so, are there any hidden pitfalls to this approach?
You certainly can place the @Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies.
The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.
You can place the @Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class. However, the mere presence of the @Transactional annotation is not enough to activate the transactional behavior.
The answer your question is no - @Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them. When using proxies, you should apply the @Transactional annotation only to methods with public visibility.
Spring uses (among others) a BeanFactoryTransactionAttributeSourceAdvisor
as an Advisor
when generating a proxy bean for classes annotated with or containing methods annotated with @Transactional
.
When the time comes to proxy it, it uses the bean's class type (with CGLIB) to generate the proxy. So we want to see if the default
method annotated with @Transactional
will be visible from the implementing class' point of view.
Here's a Java 8 SSCCE
public static void main(String[] args) throws Exception{
Class<?> randomImplClass = RandomImpl.class;
System.out.println(randomImplClass);
Easy annotation = randomImplClass.getAnnotation(Easy.class);
System.out.println("Class: " + randomImplClass);
System.out.println("Class Annotation: " + annotation);
Method method = randomImplClass.getMethod("doRandom");
annotation = method.getAnnotation(Easy.class);
System.out.println("Method: " + method);
System.out.println("Method Annotation: " + annotation);
}
public static class RandomImpl implements Random{}
@Easy
interface Random {
@Easy
default void doRandom() {System.out.println("testing");};
}
@Target(value = {METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Easy {}
which prints
class TestEnhancer$RandomImpl
Class: class TestEnhancer$RandomImpl
Class Annotation: null
Method: public default void TestEnhancer$Random.doRandom()
Method Annotation: @TestEnhancer$Easy()
Indicating that the annotation was inherited for the interface's method. It seems, therefore, that Spring will be able to add @Transactional
behavior when the class has not overriden the default
method. If it has overriden it, then annotations are not inherited.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With