前言
想的再多,不如行动起来,大家好,我是啊Q,让我们徜徉在知识的海洋里吧。
一起“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第10天, 点击查看活动详情 。
昨天我们分析了一个dubbo远程调用造成的事务回滚问题(juejin.cn/post/720072…),今天我们接着分析一下spring的事务原理和执行过程。
事务原理
Ⅰ.事务初始化
1.annotation-driven
- 1.annotation-driven 起始于TxNamespaceHandler。使用AnnotationDrivenBeanDefinitionParser处理该配置
- 2.AnnotationDrivenBeanDefinitionParser 通过调用AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext)
方法实现将事务的相关管理bean的定义加入到BeanDefinition中.
2.AnnotationTransactionAttributeSource (TransactionAttributeSource)
- 1.AnnotationTransactionAttributeSource 其作用用于处理 @Transactional(SpringTransactionAnnotationParser或其他的JTA、EJB3等注解
- 2.SpringTransactionAnnotationParser 其parseTransactionAnnotation()用于专门处理@Transactional注解.
3.TransactionInterceptor 事务拦截器(最重要).
- 1.TransactionInterceptor extend TransactionAspectSupport.
- 2.TransactionAspectSupport 的 currentTransactionStatus 是一个静态方法,外部可以直接调用用于获取到当前事务的状态,从而甚至可议手动来提交、回滚事务.
- 3.invokeWithinTransaction() 执行事务方法,这个方法是最重要的方法.
4.BeanFactoryTransactionAttributeSourceAdvisor
- 1.该方法将aop和事务结合在了一起,该方法返回一个事务切入点。因为该方法实现了advisor接口
- 2.getPointcut()方法调用加入了TransactionAttributeSource的切入点。
Ⅱ.事务初始化流程
- 1.首先是spring容器初始化bean,这个不多说了。
- 2.在初始化(initializeBean->applyBeanPostProcessorsAfterInitialization)bean的时候,找到BeanPostProcessor并执行后置处理器方法进行bean增强。
- 3.AbstractAutoProxyCreator.wrapIfNecessary()中执行了两个重要步骤:
-
- 1.getAdvicesAndAdvisorsForBean()获取advice,获取增强器。(俗话说我们要怎样增强我们的代码)。
-
-
- 1.BeanFactoryTransactionAttributeSourceAdvisor.matches() 执行获取getTransactionAttributeSource().
- 2.TransactionAttributeSource.getTransactionAttribute()方法,该方法将获取到增强方法的事务配置(TransactionAttribute)进行缓存,以待后续事务执行时使用
-
-
- 2.createProxy() 根据配置创建代理类
Ⅲ.事务执行流程
- 1.TransactionInterceptor首先我们看一下这个类的继承结构;其MethodInterceptor.invoke()方法就是执行入口。
- 2.TransactionInterceptor.invokeWithinTransaction().
-
- 1.获取TransactionAttributeSource
- 2.tas.getTransactionAttribute(method, targetClass) 获取该方法时事务配置
- 3.determineTransactionManager() 返回处理事务的事务管理器
- 4 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);重点。
创建这次事务的相关信息,添加事务属性和事务管理器,设置事务状态等。
-
-
- 1.getTransaction() 解析当前方法的事务属性定义,解析事务的传播属性。方法分为三个逻辑:没有事务抛出异常,创建事务,不需要事务。如果
存在事务,则走调用handleExistingTransaction(definition, transaction, debugEnabled)的逻辑,如果不存在事务则走下面的逻辑,按
传播事务特性处理事务。
- 1.getTransaction() 解析当前方法的事务属性定义,解析事务的传播属性。方法分为三个逻辑:没有事务抛出异常,创建事务,不需要事务。如果
-
-
- 5.invocation.proceedWithInvocation() 执行代理方法
- 6.completeTransactionAfterThrowing(txInfo, ex); 代理方法执行异常后处理异常(事务回滚等)。注意这里会抛出异常,这也是为什么父类事务会滚回的原因
- 7.cleanupTransactionInfo(txInfo); 清理事务执行信息。
- 8.commitTransactionAfterReturning(txInfo); 正常执行就提交事务。
- 3.事务传播逻辑流程图(createTransactionIfNecessary)
Ⅳ.PlatformTransactionManager
- 1.事务管理器做三件事情:getTransaction commit rollback 。 一般的默认实现是:DataSourceTransactionManager
- 2.TransactionStatus getTransaction(TransactionDefinition) 根据当前事务的事务定义获取当前事务的状态.
- 3.void commit(TransactionStatus status) 提交事务,中间进行了判断,如果不满足提交的条件将会回滚事务。
- 4.void rollback(TransactionStatus status) 回滚事务。
事务优化
- 1.只读事务可以标记为read-only.可以帮助数据库引擎优化事务
- 2.事务切面的时候注意不要切到了不需要事务的服务。(比如操作文件,远程调用等)
因为事务开启会占用数据连接,如果操作耗时,则会一直占用连接而不得释放,而造成连接词的连接数减少影响性能。
// jabc最大连接设置成: <property name="maxActive" value="1"/>
private static void test4() throws InterruptedException {
TestService testService = context.getBean(TestService.class);
new Thread(() -> testService.test6()).start(); //http请求,线程沉睡,模拟耗时不需要事务的请求
Thread.sleep(1000);
new Thread(() -> testService.test7()).start(); // 事务操作
}
@Transactional(propagation = Propagation.REQUIRED)
void test6();
@Transactional(propagation = Propagation.REQUIRED)
void test7();
- 3.尽量减小切面的大小,比如a方法中调用了5个方法,其中4个方法不需要提交事务,其中一个需要,那就可以在需要事务的方法上加事务,不要再a方法上加事务。 比如发消息通知等。
其他
savepoint
- 1.通过以下sql可以测试出savepoint点有哪些特性
begin;
insert into tx_user (username, age) values ('ll',1);
savepoint a1;
insert into tx_user (username, age) values ('ll',2);
savepoint a2;
insert into tx_user (username, age) values ('ll',3);
savepoint a3;
insert into tx_user (username, age) values ('ll',4);
ROLLBACK to a1;
select * from tx_user
- 2.特性:
-
- 1 ROLLBACK to ?; 回退到某个保存点,如果你回退到了a1,那么就不能再回退到a3了,会报错。
- 2.ROLLBACK 整个事务全部回滚。
- 3.commit之后无法再回退。
编程式事务
// 注入transactionTemplate 事务模板器
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
@Autowired
private TransactionTemplate transactionTemplate;
public void test() {
// 使用事务模板类惊醒操作
transactionTemplate.execute(t -> {
return jdbcTemplate.update(sql, new Object[]{"lisi2", "2"});
});
}