Transactional 直接放在接口方法和类方法有什么不同
在使用 @Transactional
注解时,将其放在接口方法和实现类方法上有以下不同点和注意事项:
1. 基本区别
-
接口方法上:
如果将@Transactional
注解放在接口方法上,通常需要注意:- Spring AOP 默认使用的是基于 代理(proxy) 的机制来实现事务管理。
- 接口上的注解 不会被代理类识别,因为代理类执行的是实现类的方法,而不是接口方法。
- 因此,事务实际上 不会生效。
-
实现类方法上:
将@Transactional
放在实现类的方法上是推荐的做法:- 事务会由代理对象正确识别和管理。
- Spring 会在方法调用时拦截代理对象的方法,应用事务逻辑。
2. 事务生效机制
Spring AOP 使用动态代理来管理事务:
- JDK 动态代理:
对于接口,Spring 使用基于接口的动态代理。因此,代理对象调用的是实现类的方法。- 接口上的
@Transactional
不会被代理识别。
- 接口上的
- CGLIB 动态代理:
对于没有接口的类或明确配置了使用 CGLIB,Spring 使用基于类的动态代理。- 此时事务注解需要在实现类方法或类上才能生效。
3. 推荐实践
- 将
@Transactional
注解写在 实现类的方法 或 类上。 - 如果是通过接口定义业务方法,则只需要在实现类中使用
@Transactional
即可,避免误解。
4. 实际示例
错误用法(在接口上定义事务)
public interface UserService {
@Transactional // 不推荐,事务不会生效
void saveUser(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void saveUser(User user) {
// 方法体
}
}
正确用法(在实现类上定义事务)
public interface UserService {
void saveUser(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
@Transactional // 推荐,事务会生效
public void saveUser(User user) {
// 方法体
}
}
全局事务控制
@Service
@Transactional // 全类事务控制,推荐
public class UserServiceImpl implements UserService {
@Override
public void saveUser(User user) {
// 方法体
}
}
总结
- 接口方法上的
@Transactional
对 Spring 的代理无效,事务不会生效。 - 推荐在 实现类的方法 或 类上 使用
@Transactional
注解。