当 Spring Bean 中的方法标注了 @Transactional,Spring 会自动创建代理对象来增强 Service,以保证事务生效。具体来说,Spring 默认使用代理模式来管理事务,而代理的方式取决于 Bean 是否实现了接口:
1. @Transactional 事务代理的工作原理
Spring 事务管理使用 AOP(面向切面编程) 和 代理模式,核心是:
• 基于代理的事务拦截:在调用 @Transactional 方法前,Spring 代理会开启事务,在方法执行完后提交或回滚事务。
• Spring 事务管理的代理方式:
1. JDK 动态代理(默认):如果 Service 实现了接口,Spring 使用 JDK 动态代理。
2. CGLIB 代理:如果 Service 没有实现接口,Spring 使用 CGLIB 代理(基于子类继承)。
2. Spring 事务代理的两种方式
Spring 通过 @EnableTransactionManagement 启用事务管理,默认使用 代理模式。
(1)JDK 动态代理(适用于实现了接口的类)
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println("保存用户...");
}
}
Spring 生成代理类:
UserService proxy = (UserService) applicationContext.getBean(UserService.class);
System.out.println(proxy.getClass());
🔹 输出:
class com.sun.proxy.$ProxyXX // 说明使用了JDK动态代理
✅ 代理对象是 $ProxyXX,表示 Spring 使用了 JDK 动态代理。
(2)CGLIB 代理(适用于没有实现接口的类)
如果 UserService 没有接口,Spring 使用 CGLIB 创建子类代理:
@Service
@Transactional
public class UserService {
public void saveUser() {
System.out.println("保存用户...");
}
}
Spring 生成的代理类:
UserService proxy = (UserService) applicationContext.getBean(UserService.class);
System.out.println(proxy.getClass());
🔹 输出:
class com.example.service.UserService$$EnhancerBySpringCGLIB$$xxxx
✅ Spring 通过 CGLIB 生成了 UserService 的子类,增强了事务功能。
3. 事务代理的注意点
1. 必须通过 Spring 代理调用,this.method() 事务无效
@Service
public class UserService {
@Transactional
public void saveUser() {
this.deleteUser(); // ❌ 事务不会生效!
}
@Transactional
public void deleteUser() {
System.out.println("删除用户...");
}
}
原因:
• this.deleteUser() 不会经过代理对象,事务拦截器不会生效。
• 解决方案:应该让 外部通过 Spring Bean 调用 deleteUser()。
2. 私有方法和静态方法不会被代理
@Transactional
private void deleteUser() { ... } // ❌ 事务无效
3. @Transactional 只能作用于 public 方法
• 因为代理对象只能拦截 public 方法,private 和 protected 不能被代理。
4. Spring Boot 3.x 默认使用 JDK 动态代理
• 但如果类没有接口,Spring 仍然会使用 CGLIB 代理。
• 你可以强制指定代理方式:
@EnableTransactionManagement(proxyTargetClass = true) // 强制使用 CGLIB 代理
• 或者:
spring.aop.proxy-target-class=true # 强制 CGLIB 代理
4. 代理模式和 @Transactional 事务管理的关系
• Spring 基于代理模式(JDK 动态代理 / CGLIB 代理)拦截 @Transactional 方法,自动开启和提交事务。
• 如果方法在类内部直接调用(this.方法名()),不会经过代理,事务不会生效。
• JDK 动态代理适用于接口,CGLIB 适用于没有接口的类。
浙公网安备 33010602011771号