记录开发过程中的一次@TransactionalEventListener 失效
先把代码贴出来
@Service
public class EventService {
@Autowired
@Lazy
private EventService eventService;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void test(){
eventService.aPublisher();
}
public void aPublisher(){
System.out.println("发布A事件");
Aevent aevent = new Aevent(new Object());
eventPublisher.publishEvent(aevent);
}
@Transactional
public void atest(){
System.out.println("调用atest");
eventService.bPublisher();
}
public void bPublisher(){
System.out.println("发布B事件");
Bevent bevent = new Bevent(new Object());
eventPublisher.publishEvent(bevent);
}
@Transactional
public void btest(){
System.out.println("调用btest");
}
@TransactionalEventListener
//@Async
public void aListener(Aevent aevent){
eventService.atest();
}
@TransactionalEventListener
public void bListener(Bevent aevent){
eventService.btest();
}
}
预想流程
发布A事件 > 调用atest >发布B事件>调用atest
实际流程
发布A事件 > 调用atest >发布B事件
虽然发布了Bevent事件,但是没有调用atest
一步步跟发现进入了
当 test() 执行完成后会第一次进入 TransactionSynchronizationEventAdapter#afterCompletion 此时 status =STATUS_COMMITTED
然后会执行 atest > bPublisher,
当 atest() 执完成后会第二次进入 TransactionSynchronizationEventAdapter#afterCompletion 此时 status=STATUS_UNKNOWN,不满足条件不会执行 processEvent()
org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.TransactionSynchronizationEventAdapter#afterCompletion
其中 this.phase = TransactionPhase.AFTER_COMMIT,status = STATUS_UNKNOWN =2
不满足下面的任何一条判断,所以没有调用 processEvent() 也就没有调用 回调方法
现在主要的问题是 为什么 status = STATUS_UNKNOWN
@Override
public void afterCompletion(int status) {
if (this.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {
processEvent();
}
else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
processEvent();
}
else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
processEvent();
}
}
最后定位到
org.springframework.transaction.support.AbstractPlatformTransactionManager#triggerAfterCompletion
通过分析发现 status.isNewTransaction() = false
导致进入了registerAfterCompletionWithExistingTransaction()
而 status.isNewTransaction() = false 又是因为 newTransaction = false
现在就来找 为什么newTransaction = false
private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
if (status.isNewSynchronization()) {
List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
TransactionSynchronizationManager.clearSynchronization();
if (!status.hasTransaction() || status.isNewTransaction()) {
if (status.isDebug()) {
logger.trace("Triggering afterCompletion synchronization");
}
// No transaction or new transaction for the current scope ->
// invoke the afterCompletion callbacks immediately
invokeAfterCompletion(synchronizations, completionStatus);
}
else if (!synchronizations.isEmpty()) {
// Existing transaction that we participate in, controlled outside
// of the scope of this Spring transaction manager -> try to register
// an afterCompletion callback with the existing (JTA) transaction.
//就是这里会 传入status = STATUS_UNKNOWN
registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
}
}
}
定位到
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
的
handleExistingTransaction();
会使 newTransaction = false
最后定位到是 txObject.getConnectionHolder().isTransactionActive() =true
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {
//进入这里会 把newTransaction = false
return handleExistingTransaction(definition, transaction, debugEnabled);
}
....
}
定位到
org.springframework.transaction.support.TransactionSynchronizationManager#getResource
这个方法是从当前线程中获取 ConnectionHolder
当 EventService#test() 执行完成后 ThreadLocal 就有了一个 ConnectionHolder.isTransactionActive() =true
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
//从ThreadLocal 获取当前线程的 ConnectionHolder
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
//设置ConnectionHolder
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
//ConnectionHolder.TransactionActive = true
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
//把ConnectionHolder 设置到 ThreadLoacl 中
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
现在知道在哪里造成的了最后再总结一下
status == STATUS_UNKNOWN-> newTransaction = false -> transactionActive=true -> ThreadLocal 获取当前线程的 ConnectionHolder.transactionActive=true-> 第一次进入切面会把 ConnectionHolder .transactionActive=true 设置到 ThreadLoacl 中
第一次进入 事务切面,commit 时 newTransaction = true -> status == STATUS_COMMITTED
满足 this.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED
然后进入回调方法 atest (),执行完成调用 commit 时 newTransaction = false -> status == STATUS_UNKNOWN
无法满足 回调条件 所以没有 回调 btest() ,
因为两次进入切面都有同一个线程,导致的,所以我们只需要然两次进入切面不是同一线程就可以了,那么就只需要异步监听就行了