我就是奇迹

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

当 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 适用于没有接口的类

posted on 2025-04-02 18:59  我就是奇迹  阅读(10)  评论(0)    收藏  举报