我应该将@Transactional 注释放在哪里:在接口定义或实现类中?
Posted
技术标签:
【中文标题】我应该将@Transactional 注释放在哪里:在接口定义或实现类中?【英文标题】:Where should I put @Transactional annotation: at an interface definition or at an implementing class? 【发布时间】:2011-03-08 09:38:57 【问题描述】:代码中标题的问题:
@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
...
【问题讨论】:
【参考方案1】:来自http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Spring 团队的建议是您只使用
@Transactional
注释来注释具体类,而不是注释接口。您当然可以将@Transactional
注释放在接口(或接口方法),但这仅在您使用基于接口的代理时才能正常工作。注释不被继承这一事实意味着,如果您使用基于类的代理,则基于类的代理基础架构将无法识别事务设置,并且对象不会被包装在事务代理中(这绝对是糟糕)。所以请务必听取 Spring 团队的建议,仅使用@Transactional
注释来注释具体类(以及具体类的方法)。注意:由于这种机制是基于代理的,只有通过代理传入的“外部”方法调用才会被拦截。这意味着“自调用”,即目标内的方法对象调用目标对象的一些其他方法,即使调用的方法标有
,在运行时也不会导致实际事务@Transactional
!
(在第一句中添加了重点,原文中的其他重点。)
【讨论】:
Big +1 顺便说一句,我冒昧地把这句话变成了引用,所以人们不会感到困惑,并从原文中添加了斜体等。 我之前在 Spring 文档中读过此声明,但我仍然无法理解为什么 基于类的代理基础架构无法识别事务设置?我个人认为,这只是 Spring 实现的限制,而不是底层代理基础设施的问题。 查看 Spring 源代码可以发现,JdkDynamicAopProxy
在每个方法调用上都会遍历所有 bean 顾问(另请参阅 DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()
),在声明性事务设置的情况下,包括 BeanFactoryTransactionAttributeSourceAdvisor
。依次调用TransactionAttributeSourcePointcut#matches()
,它应该收集与事务相关的信息。它被传递给目标类,它总是可以遍历这个类实现的所有接口。现在:为什么这不能可靠地工作?
@dma_k - Java 运行时只允许直接访问从超类继承的类注解,或者根本没有继承的方法注解。因此,Pointcut.matches() 必须实际递归遍历所有接口,找到具有反射的“真实”覆盖方法,处理桥接方法,重载,可变参数,泛型,代理,当然快速 .然后你必须处理菱形继承——可能许多继承的@Transactional
注释中的哪一个会适用?所以,尽管这一切都是可能的,但我并不责怪 Spring。 jira.springsource.org/browse/SPR-975
对于任何想知道的人,这仍然适用于 11 年后(春季 5.3):docs.spring.io/spring-framework/docs/5.3.x/reference/html/…【参考方案2】:
Spring 的recommendation 是您注释具体实现而不是接口。在接口上使用注解并没有错,只是可能会滥用该功能并无意中绕过您的@Transaction 声明。
如果你在接口中标记了事务性的东西,然后在 spring 的其他地方引用了它的实现类之一,那么 spring 创建的对象不会尊重 @Transactional 注释并不是很明显。
实际上它看起来像这样:
public class MyClass implements MyInterface
private int x;
public void doSomethingNonTx()
@Transactional
public void toSomethingTx()
【讨论】:
【参考方案3】:您可以将它们放在界面上,但要注意在某些情况下交易可能不会发生。请参阅 Spring 文档的 Secion 10.5.6 中的第二个提示:
Spring 建议您仅使用 @Transactional 注释来注释具体类(和具体类的方法),而不是注释接口。您当然可以将@Transactional 注释放在接口(或接口方法)上,但这仅在您使用基于接口的代理时才起作用。 Java 注释不是从接口继承的事实意味着,如果您使用基于类的代理 (proxy-target-class="true") 或基于编织的方面 (mode="aspectj"),则事务设置为不被代理和编织基础设施识别,并且对象不会被包装在事务代理中,这绝对是糟糕的。
出于这个原因,我建议将它们放在实现中。
另外,对我来说,事务似乎是一个实现细节,所以它们应该在实现类中。想象一下,拥有不需要事务性的日志记录或测试实现(模拟)的包装器实现。
【讨论】:
【参考方案4】:在具体类上支持@Transactional:
我通常更喜欢在 3 个部分中构建解决方案:API、实现和 Web(如果需要)。我尽我所能通过最小化依赖来保持 API 尽可能轻/简单/POJO。如果您在必须大量共享 API 的分布式/集成环境中播放它,这一点尤其重要。
将@Transactional 放入API 部分需要Spring 库,恕我直言,这是无效的。所以我更喜欢将它添加到运行事务的实现中。
【讨论】:
【参考方案5】:只要您的 IFC 的所有可预见实施者都关心 TX 数据(事务不仅仅是数据库处理的问题),将它放在接口上就可以了。如果该方法不关心 TX(但您需要将它放在那里以用于 Hibernate 或其他),请将其放在 impl 上。
另外,将@Transactional
放在接口中的方法上可能会更好一些:
public interface FooService
@Transactional(readOnly = true)
void doSmth();
【讨论】:
以上是关于我应该将@Transactional 注释放在哪里:在接口定义或实现类中?的主要内容,如果未能解决你的问题,请参考以下文章
在哪里放置@Transactional?在接口规范或实现中? [复制]
我必须在哪里使用注释@Service @Transactional
if-then-else 结构中的注释应该放在哪里? [关闭]