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

Rom*_*man 82 java spring annotations coding-style

代码中标题的问题:

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


public class FooServiceImpl implements FooService {
   ...
}
Run Code Online (Sandbox Code Playgroud)

VS

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}
Run Code Online (Sandbox Code Playgroud)

Rom*_*eau 112

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

Spring团队的建议是,您只使用注释注释具体类@Transactional,而不是注释接口.您当然可以将@Transactional注释放在接口(或接口方法)上,但这只会在您使用基于接口的代理时按预期工作.注释不是继承的这一事实意味着如果您使用基于类的代理,那么基于类的代理基础结构将无法识别事务设置,并且该对象将不会被包装在事务代理中(这将是非常糟糕的) .因此,请参考Spring团队的建议,并仅使用注释来注释具体类(以及具体类的方法)@Transactional.

注意:由于此机制基于代理,因此只会拦截通过代理进入的"外部"方法调用.这意味着'自调用',即目标对象中调用目标对象的其他方法的方法,即使被调用的方法被标记,也不会在运行时导致实际的事务@Transactional!

(重点加在第一句,其他重点来自原文.)

  • @dma_k-Java运行时仅提供对从超类继承的类注释或根本没有继承的方法注释的直接访问。因此,Pointcut.matches()必须实际上以递归方式遍历所有接口,找到具有反射的“真实”重写方法,处理桥方法,重载,varargs,泛型,代理,并且当然要*快速*。然后,您必须处理钻石继承-可能会应用许多继承的`@ Transactional`注释中的哪一个?因此,尽管*可能*全部,但我不怪Spring。http://jira.springsource.org/browse/SPR-975 (2认同)

Ang*_*own 9

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

Spring建议您只使用@Transactional注释来注释具体类(以及具体类的方法),而不是注释接口.您当然可以将@Transactional注释放在接口(或接口方法)上,但这只能在您使用基于接口的代理时按预期工作.Java注释不是从接口继承的事实意味着如果您使用基于类的代理(proxy-target-class ="true")或基于编织的方面(mode ="aspectj"),那么事务设置是代理和编织基础设施无法识别,并且该对象不会被包装在事务代理中,这将是非常糟糕的.

出于这个原因,我建议将它们放在实现上.

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


zmf*_*zmf 8

Spring的建议是注释具体实现而不是接口.在界面上使用注释并不正确,只能滥用该功能并无意中绕过@Transaction声明.

如果你在一个接口中标记了一些事务性的东西,然后在spring的其他地方引用它的一个实现类,那么spring创建的对象不会明显地遵循@Transactional注释.

在实践中,它看起来像这样:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}
Run Code Online (Sandbox Code Playgroud)