Is it safe to use a Spring @Transactional annotation on a Java 8 default interface method?

annotations, java, java-8, spring, transactions

Solution

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.

Problem

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?

Original source