我应该在接口定义处还是在实现类处放置 @Transactional 注解?

13 浏览
0 Comments

我应该在接口定义处还是在实现类处放置 @Transactional 注解?

标题中的问题在代码中的表示方式:\n

@Transactional(readonly = true)
public interface FooService {
   void doSmth();
}
public class FooServiceImpl implements FooService {
   ...
}

\nvs\n

public interface FooService {
   void doSmth();
}
@Transactional(readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

0
0 Comments

问题的原因是Spring推荐只在具体的类(以及具体类的方法)上使用@Transactional注解,而不是在接口上使用。如果在接口上使用@Transactional注解,只有在使用基于接口的代理时才会按预期工作。如果使用基于类的代理(proxy-target-class="true")或基于织入的切面(mode="aspectj"),那么事务设置将不会被代理和织入基础设施识别,对象也不会被包装在事务代理中。

解决方法是在实现类上使用@Transactional注解,将事务注解放在实现类上更为合适,因为事务对于接口而言是实现细节。想象一下,如果对于日志记录或测试实现(模拟)这样的包装器实现不需要进行事务处理。

所以,根据Spring的建议和事务作为实现细节的性质,我建议将@Transactional注解放在实现类上。

0
0 Comments

问题的出现原因是Spring推荐在具体的实现类上使用@Transactional注解,而不是在接口上使用。在接口上使用注解并不是错误的,只是可能会误用该功能并绕过声明。

如果你在接口上标记了@Transactional注解,然后在Spring的其他地方引用了其中一个实现类,那么显然Spring创建的对象将不会遵守该注解。

实际上,代码看起来像这样:

public class MyClass implements MyInterface { 
    private int x;
    public void doSomethingNonTx() {}
    public void toSomethingTx() {}
}

解决方法是将@Transactional注解放在具体的实现类上,而不是在接口上。这样可以确保Spring创建的对象将遵守该注解。

Spring推荐在具体的实现类上使用@Transactional注解,而不是在接口上使用。这样可以避免绕过声明的情况发生。将注解放在实现类上可以确保Spring创建的对象将遵守该注解。

0
0 Comments

问题的出现原因是Spring团队建议只在具体类上使用@Transactional注解,而不是在接口上使用。如果在接口上使用注解,只有在使用基于接口的代理时才能按预期工作。由于注解不会被继承,因此如果使用基于类的代理,则类代理基础设施将无法识别事务设置,并且对象将不会被包装在事务代理中。解决方法是按照Spring团队的建议,只在具体类及其方法上使用@Transactional注解。

从Spring文档中可以得知,在使用基于代理的机制时,只有通过代理进入的外部方法调用才会被拦截。这意味着在目标对象内部调用其他方法的"自我调用"不会在运行时导致实际的事务,即使被调用的方法标有@Transactional注解也是如此。

有人提出,在Spring的实现中,为什么"事务设置将不会被基于类的代理基础设施识别",这只是Spring的实现限制,并不是底层代理基础设施的问题。通过查看Spring源代码,可以发现JdkDynamicAopProxy在每次方法调用时会遍历所有的bean advisor,其中包括BeanFactoryTransactionAttributeSourceAdvisor,这在声明式事务设置中非常重要。TransactionAttributeSourcePointcut的matches()方法会被调用,该方法应该收集事务相关信息。它接收目标类,并且始终可以遍历该类实现的所有接口。那么,为什么这个方法无法可靠地工作呢?

答案是,Java运行时只直接提供从超类继承的类注解,或者根本没有继承的方法注解。所以,Pointcut.matches()方法必须使用反射实际递归遍历所有接口,找到"真正"的覆盖方法,并处理桥接方法、重载、可变参数、泛型、代理等,而且还必须快速完成。然后你还必须处理钻石继承——可能有多个继承的@Transactional注解适用于哪一个?所以,尽管这一切都是"可能的",但我不怪Spring。

如果在类和接口上都放置@Transactional注解,有些情况下它将无法工作,并且可能需要花费很多时间来找出问题所在。

0