我应该在接口定义或实现类中的@Transactional注解:放在哪里?


91

来自代码标题的问题:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

Answers:


121

来自http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Spring团队的建议是,您只应使用注释对具体的类进行@Transactional注释,而不是对接口进行注释。您当然可以将@Transactional注释放在接口(或接口方法)上,但这仅在您使用基于接口的代理时才可以按预期使用。注解未继承的事实意味着,如果您使用的是基于类的代理,则基于类的代理基础结构将无法识别事务设置,并且该对象也不会包装在事务性代理中(这肯定是不好的) 。因此,请务必听取Spring团队的建议,仅使用注释对具体类(以及具体类的方法)进行@Transactional注释。

注意:由于此机制基于代理,因此将仅拦截通过代理传入的“外部”方法调用。这意味着“自调用”,即目标对象中的一种调用目标对象其他方法的方法,即使被调用的方法标有@Transactional!,也不会在运行时导致实际事务。

(重点放在第一句,其他重点放在原始句中。)


大+1顺便说一句,我可以随意使用引号作为引号,这样人们就不会感到困惑,并在其后面加上了斜体等。
TJ Crowder 2010年

我在Spring文档中已经阅读过此语句,但是仍然无法理解为什么基于类的代理基础结构无法识别事务设置?我个人认为,这仅仅是Spring的实现限制,而不是底层代理基础结构的问题。
dma_k 2010年

查看Spring的源代码,可以发现JdkDynamicAopProxy遍历每个方法调用的所有bean顾问程序(另请参见DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()),在声明式事务设置的情况下,包括BeanFactoryTransactionAttributeSourceAdvisor。依次TransactionAttributeSourcePointcut#matches()调用,它应该收集与交易相关的信息。它被传递给目标类,并且它始终可以遍历该类实现的所有接口。现在:为什么这不能可靠地工作?
dma_k 2010年

2
@dma_k-Java运行时仅直接访问从超类继承的类注释,或者根本不继承的方法注释。因此,Pointcut.matches()必须实际上以递归方式遍历所有接口,找到具有反射的“真实”重写方法,处理桥方法,重载,可变参数,泛型,代理,并且当然要。然后,您必须处理钻石继承-@Transactional将应用许多继承的注释中的哪一个?因此,尽管有可能,但我不怪Spring。jira.springsource.org/browse/SPR-975
彼得·戴维斯

9

您可以将它们放在界面上,但要警告某些情况下交易可能最终不会发生。请参阅Spring文档的Secion 10.5.6中的第二个技巧:

Spring建议您仅使用@Transactional注释对具体类(以及具体类的方法)进行注释,而不是对接口进行注释。您当然可以在接口(或接口方法)上放置@Transactional批注,但这仅在您使用基于接口的代理时才可以使用。Java注释不是从接口继承的事实意味着,如果您使用的是基于类的代理(proxy-target-class =“ true”)或基于编织的方面(mode =“ aspectj”),则事务设置为代理和编织基础结构无法识别该对象,并且该对象也不会包装在事务代理中,这肯定是不好的。

因此,我建议将它们放在实现中。

另外,对我来说,事务似乎是一个实现细节,因此它们应该在实现类中。想象一下,有不需要日志记录的包装实现或测试实现(模拟),它们不需要事务性。


9

Spring的建议是注释具体的实现而不是接口。在接口上使用批注并不是不正确的,只是有可能滥用该功能并无意间绕过了@Transaction声明。

如果您已经在接口中标记了事务性的东西,然后在Spring的其他地方引用了它的实现类之一,那么spring创建的对象将不会遵守@Transactional注释并不是很明显。

实际上,它看起来像这样:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

在具体类上支持@Transactional:

我通常更喜欢在3个部分中设计解决方案:API,实现和Web(如果需要)。我通过最大程度地减少依赖关系来尽量使API保持轻/简单/ POJO。如果您在必须大量共享API的分布式/集成环境中玩游戏,这一点尤其重要。

将@Transactional放置在API部分中需要Spring库,而IMHO无效。因此,我更喜欢将其添加到运行事务的实现中。


0

只要您的IFC的所有可预见的实施者都关心TX数据,就可以将其放在接口上(事务不是数据库要处理的问题)。如果该方法不关心TX(但是您需要将其放在Hibernate或其他任何地方),则将其放在impl上。

而且,放置可能会更好 @Transactional在接口中的方法上:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.