我在服务层中使用Spring应用程序事件,以便在特定事件发生时通知更广泛的系统。我遇到的问题是事件是在事务上下文中触发的(我正在使用Spring的@Transactional
注释)。危险在于我触发事件然后事务在提交时失败(例如,在使用Hibernate时可能发生)。在这种情况下,事件侦听器将假设某些状态,然后在执行后将回滚。例如,我可能会发送电子邮件确认用户在网站上的注册,而实际上他们的用户帐户实际上并未创建。
有没有办法,保留注释的使用,标记事件提交后要触发的事件?在Java Swing中进行GUI编程时,使用SwingUtilities.invokeLater(Runnable runnable)
似乎有些类似。我想说,一旦当前事务成功提交,稍后执行这段代码。
有什么想法吗?
谢谢,
安德鲁
答案 0 :(得分:11)
这对我有用:
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// things to do when commited
}
// other methods to override are available too
});
答案 1 :(得分:4)
您可以使用TransactionSynchronizationManager而无需破解PlatformTransactionManager。
注意:TransactionAware是一个标记接口,指示ApplicationListener想要在成功提交事务后接收事件。
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);
}
}
}
}
答案 2 :(得分:2)
使用确认电子邮件解决问题的正确方法可能是使用分布式事务和参与其中的JMS资源。
但是,作为一个快速而肮脏的解决方案,您可以尝试在PlatformTransactionManager
周围创建一个包装器,它将在成功提交后执行Runnable
在ThreadLocal
存储中注册的ThreadLocal
(并在回滚后将其从AbstractPlatformTransactionManager
中删除。实际上,TransactionSynchronization
与{{1}} s具有完全相同的逻辑,但它在实现细节中埋得太深。