Spring业务默许传达行为PROPAGATION_REQUIRED:怎样做到反常被捕获处理后业务还会回滚
前语
PROPAGATION_REQUIRED是Spring默许的业务传达机制,假如当时没有业务,就新建一个业务,假如当时现已存在一个业务,参加到当时业务。
话不多说,直接上代码。
场景代码:在办法test
、insert(User user)
和insert_Exception(User user)
办法上都添加了@Transactional
,业务传达行为都是默许的PROPAGATION_REQUIRED
。
在测验办法中业务默许会主动回滚,能够在测验办法上加
@Rollback(value = false)
修正装备
@Transactional
@Rollback(value = false)
@Test
public void test(){
...
// 刺进一条记载,正常履行
userService.insert(user1);
...
try {
// 刺进一条记载,履行过程中抛出一个RuntimeException
userService.insert_Exception(user2);
} catch (Exception e) {
log.error("反常信息: {}", e.getMessage());
}
}
public class UserService {
...
@Transactional
void insert(User user) {
userMapper.insert(user);
}
@Transactional
void insert_Exception(User user) {
userMapper.insert(user);
throw new RuntimeException("抛出反常");
}
}
test
履行成果:业务回滚,刺进记载均不成功。
信任定论我们早已知晓。
userService.insert(user1)
履行正常,userService.insert_Exception(user2)
履行过程中抛出反常,反常终究在test
办法中被捕获,test
办法履行正常,那么业务是怎样知道要回滚的?
解析
我们现已在测验办法上加了@Transactional
注解,测验办法会被装备为在业务中运行,所以会履行startTransaction
。
有一个特点:
flaggedForRollback
。flaggedForRollback
是业务的回滚标志,依据这个回滚标志,会立即强制提交或回滚装备测验上下文的业务。
// 为装备的测验上下文启动一个新的业务
void startTransaction() {
...
// 现已加了@Rollback(value = false),业务不会主动回滚,值为false
this.flaggedForRollback = this.defaultRollback;
// 经过业务管理器获取当时存在的业务状况
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
...
}
经过业务管理器获取当时的业务状况transactionStatus
,首要创立一个业务目标,经过TransactionSynchronizationManager获取connectionHolder,因为当时不存在业务,所以不存在connectionHolder
。
connectionHolder:包装JDBC连接的资源持有者,而spring的业务是经过数据库连接来完结的,所以敞开一个新业务时会创立一个数据源目标来表明ConnectionHolder,作为DataSourceTransactionManager的业务目标运用。
// 依据业务目标中的connectionHolder来判别当时是否存在业务
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 业务目标中connectionHolder为空,回来false
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
因为当时不存在业务,isExistingTransaction(Object transaction)
履行成果回来false,且要敞开的业务传达行为是PROPAGATION_REQUIRED
,最终会履行startTransaction
办法,敞开新业务,符号当时业务状况transactionStatus
的newTransaction
和newConnectionHolder
为true,表明当时是一个新业务,同时也会创立一个connectionHolder
目标,该目标中包含了数据源的连接。
业务敞开后,代码履行到userService.insert(user1)
,首要获取当时的业务状况,履行doGetTransaction()
获取当时业务状况的业务目标,因为当时现已存在业务,当时业务状况的业务目标中connectionHolder不为空,履行isExistingTransaction(Object transaction)
回来true,进入handleExistingTransaction(def, transaction, debugEnabled)
办法,且userService.insert(user1)
的业务传达行为是PROPAGATION_REQUIRED
,此刻不会敞开新业务,仅仅创立一个TransactionStatus
来表明其当时的业务状况,其中newTransaction
和newConnectionHolder
为false,业务状况中的connectionHolder
与敞开业务时创立的connectionHolder
是同一个目标。
userService.insert(user1)
正常履行,然后进入commitTransactionAfterReturning(txInfo)
。
/**
* 在成功完结调用后履行
* txInfo -关于当时业务的信息
*/
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
...
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
在commit
办法中,userService.insert(user1)
现已参加外围办法的业务,依据其业务状况会履行processCommit(defStatus)
办法,这个办法会处理实践提交的业务,这儿业务没有实践提交,所以并没有履行任何操作。
以下是履行commit
对业务状况的一些判别
// status 当时业务状况
public final void commit(TransactionStatus status) throws TransactionException {
// 判别当时业务是否完结,便是说是否现已提交或者回滚
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 假如回滚标志现已设置为true,则开端回滚
if (defStatus.isLocalRollbackOnly()) {
...
processRollback(defStatus, false);
return;
}
// shouldCommitOnGlobalRollbackOnly():回来是否以大局办法对已符号为仅回滚的业务调用doCommit,默许值:false。假如 // rollbackOnly符号被设置为true,那么业务就会回滚而不会提交
// defStatus.isGlobalRollbackOnly(),判别rollbackOnly的值
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
...
}
// 处理回滚操作
processRollback(defStatus, true);
return;
}
// 提交业务
processCommit(defStatus);
}
持续履行到userService.insert_Exception(user2)
,这儿也会参加外围办法的业务,创立一个业务状况目标,操作和userService.insert(user1)
一样,可是在办法履行过程中抛出反常,这儿不会履行commitTransactionAfterReturning
,变成履行completeTransactionAfterThrowing(txInfo, ex)
。
开端处理回滚操作,履行doSetRollbackOnly
办法,将ConnectionHolder的仅回滚符号rollbackOnly
设置为为true
,最终userService.insert_Exception(user2)
抛出反常到外围办法,反常在外围办法中被捕获并处理。
这时因为现已处理了userService.insert_Exception(user2)
抛出的反常,测验办法test是正常履行完结的,而且之前添加了@Rollback(value = false)
,将flaggedForRollback设置为false,业务并不会主动回滚,所以业务开端履行提交操作,可是在处理业务提交之前,会依据业务状况(status
中的rollbackOnly
)和大局业务的仅回滚符号(connectionHolder
中的rollbackOnly
)别离进行判别是否要进行回滚操作。
假如
this.flaggedForRollback
的值为true,那么在endTransaction()
会直接履行rollback
办法,不会履行commit
办法,在rollback
办法中会履行processRollback(defStatus, false)
,然后完结业务回滚。
// 依据回滚标志,立即强制提交或回滚装备测验上下文的业务
void endTransaction() {
...
try {
// 因为之前添加了@Rollback(value = false),将flaggedForRollback设置为false,这儿会履行提交办法
if (this.flaggedForRollback) {
this.transactionManager.rollback(this.transactionStatus);
}
else {
this.transactionManager.commit(this.transactionStatus);
}
}
...
}
// status 当时业务状况
public final void commit(TransactionStatus status) throws TransactionException {
...
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 经过业务状况的仅回滚标志来判别是否回滚
if (defStatus.isLocalRollbackOnly()) {
...
// 处理回滚操作,参数unexpected为false
processRollback(defStatus, false);
return;
}
// 经过判别大局业务自身被业务和谐器是否被符号为回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
...经过查看业务目标确定仅回滚标志
// 处理回滚操作,参数unexpected为true
processRollback(defStatus, true);
return;
}
// 提交业务
processCommit(defStatus);
}
在上面的代码中,有两处代码会判别仅回滚标志rollbackOnly
,可是终究都会履行processRollback
来回滚业务,那么processRollback(defStatus, false)
和processRollback(defStatus, true)
会履行哪个办法,为什么它们的第二个参数(参数名:unexpected)又有什么不同呢?
首要解释一下为什么仅回滚标志rollbackOnly
能决议现有业务的回滚:
在业务管理器中,有一个特点globalRollbackOnParticipationFailure
,默许值为true,用来设置是否在参与的业务失利后将现有业务大局符号为仅回滚。而在shouldCommitOnGlobalRollbackOnly()
办法中,决议了是否以大局办法对已符号为仅回滚的业务调用doCommit
,也便是履行提交操作,其默许值为false。
因此,在这种情况下(设置了仅回滚标志为true),业务管理器将触发回滚,然后抛出一个unexpectedRollbackexception
。
那么以上代码会履行哪个办法?
defStatus
指的是test办法的业务状况,defStatus.isLocalRollbackOnly()
查看的是defStatus
的仅回滚符号rollbackOnly
,test办法正常履行成功,业务状况中的仅回滚符号为false。
想一想在办法履行失利时是怎样设置仅回滚符号rollbackOnly
的,没错,便是在connectionHolder
,所以查看大局业务是否被符号为回滚便是查看connectionHolder
中的仅回滚符号rollbackOnly
。
// defStatus.isGlobalRollbackOnly()的完结
@Override
public boolean isRollbackOnly() {
return getConnectionHolder().isRollbackOnly();
}
为什么processRollback
处理回滚操作,参数unexpected一个为true,一个为false?
unexpected
,便是意外的意思。
-
processRollback(defStatus, true)
:test办法正常履行完结,原本要履行提交操作,可是大局业务自身被业务和谐器被符号为回滚,所以变为履行回滚操作,unexpected
为true表明这是意外回滚。简而言之,原本要提交,最终回滚了,这便是意外回滚,所以最终也会抛出意外回滚反常。 -
processRollback(defStatus, false)
:办法履行过程中抛有反常未处理导致业务回滚时,unexpected
为false,unexpected
为为false,表明该回滚并不意外。
总结
一言以蔽之:在业务传达行为都是PROPAGATION_REQUIRED
的情况下,假如办法敞开了新业务,首要会获取当时业务状况,没有connectionHolder
就会创立一个,假如办法内部调用业务办法,被调用的办法都会参加当时业务,共用同一个connectionHolder
,假如被调用的办法出现反常而导致回滚,就会设置connectionHolder
中的仅回滚符号rollbackOnly
为真,便是符号大局业务为仅回滚,这样就算在外围办法中处理了被调用办法抛出的反常,终究业务仍是会履行回滚操作。
OK,就这样吧,有过错的地方能够指出,欢迎讨论!