transaction 第二部分
之所以称之为第二部分是因为这部分主要在 JdkDynamicAopProxy 类,之后的操作。第一次读源码,有啥偏差的地方,大虾们拍砖拍砖……
时序图
- 开始之前先看一个时序图(画的不好,估计也就我自己看得懂 emmmmmmm)
这个图,一个简单的流程。如果非要说UserService.save()
方法为啥直接到JdkDynamicAopProxy.invoke()
方法。那是动态代理的结果,也叫动态增强,关于那部分的内容下篇博客会概括,预计2天后的周五吧。还有,这样类似的博客有很多
环境配置 (部分)
- Spring xml配置文件
<tx:annotation-driven/>
<bean id="userService" class="com.yhj.jdbc.service.impl.UserServiceI">
<property name="dataSource" ref="dataSource"/>
<property name="gameService" ref="gameService"/>
</bean>
<bean id="gameService" class="com.yhj.jdbc.service.impl.GameServiceI"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- Java 实现类
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class GameServiceI implements GameService {
@Override
public int queryALlGames() {
System.out.println("queryALlGames");
throw new RuntimeException("手动抛出的异常");
}
}
@Transactional(propagation = Propagation.REQUIRED)
public class UserServiceI implements UserService {
private JdbcTemplate jdbcTemplate;
private GameService gameService;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setGameService(GameService gameService) {
this.gameService = gameService;
}
@Override
public void save(User user) {
try {
gameService.queryALlGames();
} catch (Exception e) {
e.printStackTrace();
}
jdbcTemplate.update("INSERT INTO user(studentname,studentnumber) VALUES (?,?)", new Object[]{user.getEmployeeid(), user.getUsername()}, new int[]{Types.VARCHAR, Types.VARCHAR});
}
}
干正事
再声明一下,因为动态代理的时候,会涉及到一些类的注册,从静态代理开始可能会比较容易不知道,注册的类是用来干嘛的以及在什么时候用。
代理时:在JdkDynamicAopProxy.invoke()
方法的如下代码:
1. 获取 TransactionInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
通过从
BeanFactoryTransactionAttributeSourceAdvisor
获取TransactionInterceptor
(在解析自定义标签时注入)。
- 通过
ReflectiveMethodInvocation
调用TransactionInterceptor.invoke()
方法
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
跟踪代码到 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction()
方法。参见部分代码:
- 获取事务属性
如:PROPAGATION_REQUIRED,ISOLATION_DEFAULT - 获取事务管理器
transactionManager
org.springframework.transaction.support.AbstractPlatformTransactionManager
- 获取连接点
如:com.yhj.jdbc.service.impl.UserServiceI.save()
- 创建一个事务,方法
createTransactionIfNecessary()
- 调用 service 中被代理的方法
- 异常处理
- 清理资源
- 提交事务
乍一看,就是这样,已经没有了……
获取事务 createTransactionIfNecessary
在这个方法内部通过 AbstractPlatformTransactionManager.getTransaction()
处理了各种事务逻辑。如下图:
可以看出来,如果当前已经存在(当前线程
ConnectionHolder
熟悉不为空且isTransactionActive()
为true
)事务就直接通过handleExistingTransaction
方法返回了。从代码中也明显能看出来,只有
PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED
才对事务有支持。doBegin()
方法:主要设置当前线程连接的ConnectionHloder,timeout,isolation
如果时新连接还需要绑定资源,在这个方法里面,关闭了 jdbc 的自动提交,由 Spring 控制事务。
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
当前线程已经存在事务 handleExistingTransaction
Spring 中时允许事务嵌套的,关于嵌套事务的处理就在 handleExistingTransaction()
方法中。Spring 中代码如下:
这里面都会把当前的事务挂起,所谓的挂起就清空当前的事务状态,然后创建一个新的 SuspendedResourcesHolder
对象。
事务挂起 suspend
关键代码如下:
这里面都会把当前的事务挂起,所谓的挂起就清空当前的事务状态,然后创建一个新的
SuspendedResourcesHolder
对象
异常处理 completeTransactionAfterThrowing
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
// org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
可以看出,默认只对 RuntimeException
和 Error
回滚。
处理回滚的代码: org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
如果是一个新的事务,就直接执行回滚。若不是,则发生异常的时候就会把当前事务的状态设置为
readOnly
。所以一个事务只能被提交一次,如果是嵌套事务,就会通过cleanupAfterCompletion()
方法恢复被挂起的事务,详情如下:
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
- 如题:如果有被挂起的事务,就将其恢复。关于事务提交和回滚的,我就目前只关注了上面这些内容,其实里面有一些触发器,还是比较重要。