@Transational事务注解底层原理以及什么场景事务会失效

@Transactional的底层是如何实现的

底层是通过动态代理 实现的。Spring Boot 在运行时会生成一个代理对象 ,该代理对象被注解的方法调用,并在方法调用前后进行事务管理 ,事务管理包括开启事务,提交事务或回滚事务等操作。

1开启事务

2执行业务逻辑

3提交/回滚事务


@Transational中常见的参数有哪些

value-指定bean的名称

指定事务管理器的 bean 名称。当应用中存在多个事务管理器时,可以通过该参数指定要使用的事务管理器

复制代码
@Transactional(value = "myTransactionManager")
public void someMethod() {
    // 业务逻辑
}

propagation-指定Spring事务的传播行为

指定事务的传播行为,即当一个事务方法被另一个事务方法调用时,事务如何进行传播。Spring 定义了 7 种传播行为,常用的有以下几种:

  • Propagation.REQUIRED(默认值):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • Propagation.REQUIRES_NEW:无论当前是否存在事务,都会创建一个新的事务,并且挂起当前事务(如果存在)。

  • Propagation.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则挂起当前事务。

  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

  • Propagation.MANDATORY:表示该方法必须在一个事务中运行,如果当前没有事务,则抛出异常。

  • Propagation.NEVER:表示该方法不能在一个事务中运行,如果当前存在事务,则抛出异常。

  • Propagation.NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void someMethod() {
    // 业务逻辑
    }


timeout-事务的超时时间

复制代码
@Transactional(timeout = 5)
public void someMethod() {
    // 业务逻辑
}

readOnly-事务只读不可进行写操作

复制代码
@Transactional(readOnly = true)
public void someMethod() {
    // 只读业务逻辑
}

rollbackFor-遇到哪些异常回滚

指定哪些异常类型会导致事务回滚。可以指定多个异常类型,用逗号分隔

复制代码
@Transactional(rollbackFor = {SQLException.class, MyCustomException.class})
public void someMethod() throws SQLException, MyCustomException {
    // 业务逻辑
}

什么情况会导致@Transactional失效

1.添加到非public方法上

2.使用try catch处理异常

3.调用类内部的Transactional注解(嵌套使用)

4.事务的传播机制使用不当

5.数据库不支持事务


为什么@Transational注解内部调用会导致事务失效呢?

Spring 的 @Transactional 注解是基于 AOP(面向切面编程)实现的。AOP 通过代理模式为目标对象创建代理对象,在代理对象中织入事务管理的逻辑,例如在方法调用前后开启、提交或回滚事务

当外部调用带有 @Transactional 注解的方法时,实际上是调用的代理对象的方法,代理对象会处理事务相关的操作

当在同一个类的一个方法中调用另一个带有 @Transactional 注解的方法时,这种调用属于内部调用

内部调用时,并没有经过代理对象,而是直接调用了目标对象的方法,绕过了 AOP 代理的事务增强逻辑,从而导致事务失效


为什么参数rollbackFor要用Exception.class

如果不配置Exception.class的话,事务只会遇到RunTimeException的时候回滚

如果配置了的话,那么事务遇到非运行时异常时也回滚

也就是我们的Exception包含了所有异常,这个的意思是我们遇到异常就回滚,而不是遇到特定的运行时异常RunTimeException的时候回滚


如果想要嵌套事务调用@Transational修饰的方法生效该怎么办

方法一:注入自身 Bean

在类中通过依赖注入自身的 Bean,然后使用注入的 Bean 进行内部方法调用,这样就可以通过代理对象调用方法,使事务注解生效

因为我们Bean注入了,所以这个对象可以通过代理对象调用方法

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    // 注入自身的 Bean
    @Autowired
    private UserService self;

    public void outerMethod() {
        // 通过注入的 Bean 调用带有 @Transactional 注解的方法
        self.innerMethod();
    }

    @Transactional
    public void innerMethod() {
        // 业务逻辑
        System.out.println("执行内部方法的业务逻辑,事务生效");
    }
}

方法二:使用 AopContext.currentProxy() 获取代理对象

在配置类中开启 exposeProxy = true,之后在代码里使用 AopContext.currentProxy() 获取当前代理对象,再通过代理对象调用方法

复制代码
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

// 配置类,开启 exposeProxy
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
class AopConfig {
    // 可以添加其他配置
}

@Service
public class UserService {

    public void outerMethod() {
        // 获取代理对象并调用带有 @Transactional 注解的方法
        ((UserService) AopContext.currentProxy()).innerMethod();
    }

    @Transactional
    public void innerMethod() {
        // 业务逻辑
        System.out.println("执行内部方法的业务逻辑,事务生效");
    }
}

方法三:getBean方法获取自身Bean实例

这种方法是和注入Bean是一样的

借助 ApplicationContext 获取 Bean 实例。首先要将 ApplicationContext 注入到服务类中,然后使用 getBean 方法获取自身 Bean 实例,再通过该实例调用带有 @Transactional 注解的方法

复制代码
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void outerMethod() {
        // 通过 getBean 方法获取自身 Bean 实例
        UserService self = applicationContext.getBean(UserService.class);
        // 调用带有 @Transactional 注解的方法
        self.innerMethod();
    }

    @Transactional
    public void innerMethod() {
        // 业务逻辑
        System.out.println("执行内部方法的业务逻辑,事务生效");
    }
}
相关推荐
二哈赛车手8 小时前
新人笔记---ApiFox的一些常见使用出错
java·笔记·spring
栗子~~9 小时前
JAVA - 二层缓存设计(本地缓冲+redis缓冲+广播所有本地缓冲失效) demo
java·redis·缓存
YDS8299 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— RAG知识库的搭建和接口实现
java·ai·springboot·agent·rag·deepseek
rising start9 小时前
二、全面理解MySQL架构
mysql·架构
星星也在雾里10 小时前
PgBouncer 解决 PostgreSQL 连接数超限 + 可视化监控
数据库·postgresql
bqq1986102610 小时前
MySQL性能优化
mysql·mysql优化
未若君雅裁10 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis
AI人工智能+电脑小能手11 小时前
【大白话说Java面试题 第65题】【JVM篇】第25题:谈谈对 OOM 的认识
java·开发语言·jvm
阿维的博客日记11 小时前
Nacos 为什么能让配置动态生效?(涉及 @RefreshScope 注解)
java·spring
雨辰AI11 小时前
SpringBoot3 + 人大金仓读写分离 + 分库分表 + 集群高可用 全栈实战
java·数据库·mysql·政务