@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("执行内部方法的业务逻辑,事务生效");
}
}