开发者

Spring Transaction Annotations - Execute on Success

开发者 https://www.devze.com 2023-01-12 05:29 出处:网络
I\'m using Spring application events within my service layer to notify the wider system when specific events occur. The issue I have is that the events are fired within a transactional context (I\'m u

I'm using Spring application events within my service layer to notify the wider system when specific events occur. The issue I have is that the events are fired within a transactional context (I'm using Spring's @Transactional annotation). The danger is that I fire an event and then the transaction fails on commit (can happen when using, for example, Hibernate). In this scenario, the event listeners will be assuming some state that will then be rolled back after they've executed. The danger here is that I may, for example, have sent an email to confirm a user's registration on a website when, in fact, their user account was not actually created.

Is there a way, preserving the use of annotations, to somewhere flag an event to be fired after the transaction commits? It kind of seems somewhat analogous to using SwingUtilities.invokeLater(Runnable runnable) when doing GUI programming in Java Swing. I want to say, execute this bit of code later on once the current transaction开发者_运维问答 commits successfully.

Any ideas?

Thanks,

Andrew


This works for me:

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            // things to do when commited
        }
        // other methods to override are available too
    });


You can make use of TransactionSynchronizationManager without needing to hack PlatformTransactionManager.

Note: TransactionAware is a marker interface indicating that a ApplicationListener wants to receive a Event after transaction is committed successfully.

public class TransactionAwareApplicationEventMulticaster extends SimpleApplicationEventMulticaster {

    @Override
    public void multicastEvent(ApplicationEvent event) {
        for (ApplicationListener listener : getApplicationListeners(event)) {
            if ((listener instanceof TransactionAware) && TransactionSynchronizationManager.isSynchronizationActive()) {
                TransactionSynchronizationManager.registerSynchronization(
                    new EventTransactionSynchronization(listener, event));
            }
            else {
                notifyEvent(listener, event);
            }
        }
     }

     void notifyEvent(final ApplicationListener listener, final ApplicationEvent event) {
          Executor executor = getTaskExecutor();
          if (executor != null) {
               executor.execute(new Runnable() {
                    public void run() {
                         listener.onApplicationEvent(event);
                    }
               });
          }
          else {
               listener.onApplicationEvent(event);
          }
     }

    class EventTransactionSynchronization extends TransactionSynchronizationAdapter {
        private final ApplicationListener listener;
        private final ApplicationEvent event;

        EventTransactionSynchronization(ApplicationListener listener, ApplicationEvent event) {
            this.listener = listener;
            this.event = event;
        }

        @Override
        public void afterCompletion(int status) {
            if ((phase == TransactionPhase.AFTER_SUCCESS)
                    && (status == TransactionSynchronization.STATUS_COMMITTED)) {
                notifyEvent(listener, event);
            }
        }
    }
}


The proper way to solve your problem with confirmation emails is probably to use a distributed transactions with a JMS resource participating in them.

However, as a quick-and-dirty solution, you may try to create a wrapper around a PlatformTransactionManager which will execute Runnables registered in some ThreadLocal storage after successfull commit (and remove it from ThreadLocal after rollback). Actually, AbstractPlatformTransactionManager have exactly this kind of logic with TransactionSynchronizations, but it's buried too deep into its implementation details.

0

精彩评论

暂无评论...
验证码 换一张
取 消