2013-12-09 68 views
12

Spring文档建议不要将@Transactional注释放在接口方法上,因为接口注释不能被类继承。但是,对于Java 8,我们可以在界面中提供具体的默认实现。如果这种默认的接口方法需要作为事务边界,我们别无选择:我们必须在接口方法上放置@Transactional注解。在Java 8默认接口方法上使用Spring @Transactional注释安全吗?

这项工作(即在这种情况下是否会尊重事务边界)?如果是这样,这种方法有没有隐藏的陷阱?

+0

如果您需要方法的默认实现,为什么不创建抽象类而不是接口? Java 8 API文档使我相信,接口默认方法主要是为了允许在接口中添加新方法而不破坏该接口的旧版本实现。 – VGR

+0

接口默认方法允许您创建[mixins](https://en.wikipedia.org/wiki/Mixin),这是一个比传统的java接口更强大的编程结构。是否有合理的理由在事务中使用混合服务接口是另一个问题,但这不是不可想象的。使用抽象类也是可行的,但将您限制为线性对象层次结构。 Mixins更灵活。 – JMB

回答

10

当生成代理Bean时,Spring使用(其中包括)一个BeanFactoryTransactionAttributeSourceAdvisor作为Advisor,该代理bean用于注释或包含用@Transactional注释的方法。

当需要代理它时,它使用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() 

表明注释被继承的接口的方法。因此,当类没有覆盖default方法时,Spring似乎能够添加@Transactional行为。如果它覆盖了它,则注释不会被继承。

+0

非常好的测试。更进一步,您可以重写RandomImpl中的默认实现,并证明您必须将注释放在覆盖方法上才能看到它。我不认为这与使用继承有什么不同。 – JMB

+0

@JMB对,我在本地做过,但没有在这里发布示例。最后一句覆盖了它。稍后回家时我会添加它。 –

相关问题