Spring 事务解析
什么是事务
事务是指数据库管理系统执行的一组操作,作为一个逻辑单元呈现,要么全部成功地执行,要么全部不执行。事务的目的是保证数据库在并发操作时的一致性和完整性。
数据库事务应满足的四个特性:
- 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部执行成功,要么全部回滚到事务开始前的状态,没有中间状态。如果在事务执行过程中发生错误,所有已执行的操作都必须回滚,使数据库恢复到原始状态。
- 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。这意味着事务在执行过程中对数据的修改必须符合预定义的规则,以保证数据库的一致性。
- 隔离性(Isolation):事务的执行是相互隔离的,一个事务的执行不能影响其他事务的执行。隔离性确保每个事务在并发执行时都能像被串行执行一样,避免了并发操作导致的数据不一致问题。
- 持久性(Durability):一旦事务提交,其所做的修改将永久保存在数据库中,即使发生系统故障或重新启动数据库系统,也能保证数据的持久性。
Spring 中的事务基本原理
- 首先从
@EnableTransactionManagement
注解开始
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 在开启事务的注解中,引入了 TransactionManagementConfigurationSelector 类
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
// 默认值为false。如果设置为true,则表示强制使用基于类(CGLIB)的代理而不是基于接口(JDK)的代理。当目标对象没有实现任何接口时,必须使用CGLIB代理。
boolean proxyTargetClass() default false;
// 默认值为AdviceMode.PROXY。
// 表示使用哪种AOP代理模式,可选值为AdviceMode.PROXY和AdviceMode.ASPECTJ。
// 当使用AdviceMode.PROXY时,Spring将创建基于JDK的动态代理或基于CGLIB的代理;
// 当使用AdviceMode.ASPECTJ时,Spring将使用AspectJ编译器生成的字节码。
AdviceMode mode() default AdviceMode.PROXY;
// 默认值为Ordered.LOWEST_PRECEDENCE。表示定义切面的优先级,用于指定多个切面的执行顺序。值越小,优先级越高。
int order() default Ordered.LOWEST_PRECEDENCE;
}
- 进入TransactionManagementConfigurationSelector类中,该类中的核心方法是
selectImports
,该方法的返回值是数组,会将返回值注册到 Spring 容器中。
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// 默认是PROXY
// 可以发现,此处主要是往Spring中注入了两个bean,分别是AutoProxyRegistrar和ProxyTransactionManagementConfiguration
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
// 表示不用动态代理技术,用ASPECTJ技术,比较麻烦了
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
-
解析
AutoProxyRegistrar
类,通过该方法的不断深入,会发现最终是往 Spring 中注入了InfrastructureAdvisorAutoProxyCreator
类,该类的继承关系如下:
该类实现了BeanPostProcessor
接口,在 Spring 的生命周期中,BeanPostProcessor
接口是用于初始化后操作的接口,在 Spring 进行扫描过程中,会扫描出该接口的所有实现类,并执行它的后置处理方法postProcessAfterInitialization
。 -
解析
postProcessAfterInitialization
方法,该方法的实现并没有放在InfrastructureAdvisorAutoProxyCreator
中,而是放在了AbstractAutoProxyCreator
中去进行实现,若该 bean 可以被 Advisors 匹配,则会生成代理对象返回。
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判断是否符合代理条件,若符合,执行代理
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aop
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 当前正在创建的Bean不用进行AOP,比如切面Bean
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象
// 此处根据类以及类中的方法去匹配到Interceptor(也就是Advice),然后生成代理对象,代理对象在执行的时候,还会根据当前执行的方法去匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// advisedBeans记录了某个Bean已经进行过AOP了
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
- 回到第二步,解析
ProxyTransactionManagementConfiguration
类,该类中包含三个方法,其实就是创建了三个 bean。
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// AnnotationTransactionAttributeSource中定义了一个Pointcut
// 创建用于解析@Transactional注解的解析器。
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
- 解析
TransactionInterceptor
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// TransactionAttribute就是@Transactional中的配置
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取@Transactional注解中的属性值
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 返回Spring容器中类型为TransactionManager的Bean对象,这块使用到了模板模式
final TransactionManager tm = determineTransactionManager(txAttr);
// ReactiveTransactionManager用得少,并且它只是执行方式是响应式的,原理流程和普通的是一样的
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
...
}
// 把tm强制转换为PlatformTransactionManager,所以我们在定义时得定义PlatformTransactionManager类型
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// joinpoint的唯一标识,就是当前在执行的方法名字
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// CallbackPreferringPlatformTransactionManager表示拥有回调功能的PlatformTransactionManager,也不常用
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建事务,在这里面进行传播行为的处理
// TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 执行下一个Interceptor或被代理对象中的方法
retVal = invocation.proceedWithInvocation(); // test() sql
}
catch (Throwable ex) {
// target invocation exception
// 抛异常了,则回滚事务,或者
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
...
}
}
- 解析
transactionAdvisor
方法,该方法主要就是在 Spring 中获取切点和通知进行组装,然后在 Bean 的初始化后流程中,进行代理,处理事务。
以上便是 Spring 声明式事务的处理逻辑。
Spring 事务失效的基本场景
- 方法没有被代理:Spring 的事务管理是通过 AOP 实现的,只有被代理的方法才能被事务拦截器拦截并进行事务管理。如果一个类的方法没有被代理,那么事务管理将不会生效。如下场景:
public class Demo {
@Transactional(rollbackFor = Exception.class)
public void methodA() {
System.out.println("执行数据库操作1...");
System.out.println("执行数据库操作2...");
System.out.println("执行数据库操作3...");
}
}
Demo demo = new Demo();
demo.methodA();
- 不同类中的方法调用:事务默认是基于方法的,当一个事务方法A内部调用另一个类的方法B时,如果B方法没有标注@Transactional注解,那么它将在自己的独立事务中运行,而不受A方法的事务控制。如下场景:
@Service
class Demo {
public void methodA() {
methodB();
}
@Transactional(rollbackFor = Exception.class)
public void methodB() {
System.out.println("执行数据库操作1...");
System.out.println("执行数据库操作2...");
System.out.println("执行数据库操作3...");
}
}
demo.methodA();
- 异常没有被捕获:事务默认只有在遇到未被捕获的 RuntimeException 或 Error 时才会回滚,而对于已捕获的异常,默认情况下是不会回滚事务的。如果捕获了异常但没有进行适当的处理或回滚,事务可能会失效。如下场景:
@Service
class Demo {
@Transactional(rollbackFor = Exception.class)
public void methodC() {
try {
int i = 1/0;
} catch (Exception e) {
e.printStackTrace();
}
}
}
demo.methodC();
- 异步方法:Spring 中的事务默认是不支持异步方法的。如果在异步方法上标注了 @Transactional 注解,事务将不会被应用。需要使用特殊的配置来支持事务在异步方法中的生效。如下场景:
@Service
class Demo {
@Transactional(rollbackFor = Exception.class)
@Async
public void methodB() {
System.out.println("执行数据库操作1...");
System.out.println("执行数据库操作2...");
System.out.println("执行数据库操作3...");
}
-
事务传播行为设置不正确:事务的传播行为定义了事务方法与其他事务方法的相互关系。如果传播行为设置不正确,例如将一个事务方法嵌套在一个不支持事务的方法中,或者将一个事务方法调用标记为 REQUIRES_NEW(每次都创建新的事务),可能会导致事务失效。
-
事务超时:如果事务执行的时间超过了设置的事务超时时间,事务可能会被强制回滚,导致事务失效。
-
数据库不支持事务:某些数据库引擎可能不支持事务,或者配置不正确,导致事务失效。如 MyISAM、MEMORY,