在Java 8默认接口方法上使用Spring @Transactional注解是否安全?

19

Spring文档建议不要在接口方法上放置@Transactional注释,因为接口注释不会被类继承。但是,在Java 8中,我们可以在接口中提供具体的默认实现。如果这样的默认接口方法需要成为事务边界,我们别无选择:必须在接口方法上放置@Transactional注释。

这种情况下,这样做是否有效(即Spring是否会尊重事务边界)?如果是,则存在任何隐藏的缺陷吗?


如果您需要一个方法的默认实现,为什么不创建一个抽象类而不是接口呢?Java 8 API文档让我相信,接口默认方法主要用于允许向接口添加新方法,而不会破坏该接口的旧实现。 - VGR
接口默认方法允许您创建mixins,这是一种比传统Java接口更强大的编程构造。在服务接口中使用Mixin是否有有效的理由与事务无关,但这并非不可想象。使用抽象类也可以实现,但会限制您的对象层次结构为线性。Mixin更加灵活。 - JMB
1个回答

15

Spring在生成带有或包含使用@Transactional注解的方法的类的代理bean时,使用(其中之一)BeanFactoryTransactionAttributeSourceAdvisor作为Advisor

当代理它的时候,它使用bean的类类型(使用CGLIB)来生成代理。因此,我们想确定从实现类的角度是否可以看到带有@Transactional注解的default方法。

这里是一个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 {}

打印输出

class TestEnhancer$RandomImpl
Class: class TestEnhancer$RandomImpl
Class Annotation: null
Method: public default void TestEnhancer$Random.doRandom()
Method Annotation: @TestEnhancer$Easy()
表示该注解是从接口的方法继承而来。因此,似乎当类没有覆盖默认方法时,Spring 将能够添加 @Transactional 行为。如果它已经覆盖了默认方法,则注解不会被继承。

非常好的测试。为了更进一步,您可以覆盖RandomImpl中的默认实现,并演示必须在覆盖方法上放置注释才能看到它的内容。我认为这与使用继承没有任何区别。 - JMB
@JMB 没错,我在本地做了,但没有在这里发布示例。最后一句话已经涵盖了它。我晚些时候回家后会添加它。 - Sotirios Delimanolis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接