起因:有时候我们有这样的需求,在一个事务方法中调用了其他类的某个方法,但希望这个方法再事务提交后执行。
思考:被调用的方法,肯定要做到延迟执行,可以把执行的方法放入ThreadLocal里,等事务提交后拿出来执行,但怎么判断事务已经提交了呢,这个就要用到spring 事务处理的AbstractPlatformTransactionManager类里面的回调功能
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
上面的trtriggerAfterCommit(status);就是处理事务提交后的动作处理,继续跟踪发现是调用
TransactionSynchronizationUtils 的下面方法
public static void invokeAfterCommit(List<TransactionSynchronization> synchronizations) {
if (synchronizations != null) {
for (TransactionSynchronization synchronization : synchronizations) {
synchronization.afterCommit();
}
}
}
synchronizations参数是通过 TransactionSynchronizationManager.getSynchronizations()获得的真是从ThreadLoca获取的对象是TransactionSynchronization。
解决方法:自定义注解,在切面拦截有注解 的方法,并把待调用的方法封装到下面的这个类里并且执行execute方法,把当前方法设置到TransactionSynchronizationManager。最后再回调方法执行。
public class AfterCommitExecutorImpl extends TransactionSynchronizationAdapter implements AfterCommitExecutor {
private static final Logger LOGGER = Logger.getLogger(AfterCommitExecutorImpl.class);
private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<List<Runnable>>();
@Override
public void execute(Runnable runnable) {
LOGGER.info("Submitting new runnable {"+runnable+"} to run after commit");
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
LOGGER.info("Transaction synchronization is NOT ACTIVE. Executing right now runnable {"+runnable+"}");
runnable.run();
return;
}
List<Runnable> threadRunnables = RUNNABLES.get();
if (threadRunnables == null) {
threadRunnables = new ArrayList<Runnable>();
RUNNABLES.set(threadRunnables);
TransactionSynchronizationManager.registerSynchronization(this);
}
threadRunnables.add(runnable);
}
@Override
public void afterCommit() {
List<Runnable> threadRunnables = RUNNABLES.get();
LOGGER.info("Transaction successfully committed, executing {"+threadRunnables.size()+"} runnables" );
for (int i = 0; i < threadRunnables.size(); i++) {
Runnable runnable = threadRunnables.get(i);
LOGGER.info("Executing runnable {"+runnable+"}");
try {
runnable.run();
} catch (RuntimeException e) {
LOGGER.error("Failed to execute runnable " + runnable, e);
}
}
}
@Override
public void afterCompletion(int status) {
LOGGER.info("Transaction completed with status {"+(status == STATUS_COMMITTED ? "COMMITTED" : "ROLLED_BACK")+"}");
RUNNABLES.remove();
}