Spring Аннотации транзакций - выполнение при успешном завершении
Я использую события приложения Spring на моем уровне обслуживания, чтобы уведомлять более широкую систему о возникновении определенных событий. Проблема заключается в том, что события запускаются в транзакционном контексте (я использую аннотацию Spring @Transactional
). Опасность состоит в том, что я запускаю событие, а затем транзакция завершается с ошибкой при совершении (может произойти при использовании, например, Hibernate). В этом случае слушатели событий будут принимать какое-то состояние, которое затем будет откатываться после их выполнения. Опасность здесь заключается в том, что я могу, например, отправить электронное письмо, чтобы подтвердить регистрацию пользователя на веб-сайте, когда на самом деле их учетная запись пользователя фактически не была создана.
Есть ли способ, сохраняющий использование аннотаций, где-то флаг события, которое должно быть запущено после совершения транзакции? Кажется, это похоже на использование SwingUtilities.invokeLater(Runnable runnable)
при программировании GUI в Java Swing. Я хочу сказать, выполнить этот бит кода позже, как только текущая транзакция завершится успешно.
Любые идеи?
Спасибо,
Эндрю
Ответы
Ответ 1
Правильный способ решить вашу проблему с помощью писем с подтверждением, вероятно, заключается в использовании распределенных транзакций с участием участвующих в них ресурсов JMS.
Однако, как быстрое и грязное решение, вы можете попытаться создать обертку вокруг PlatformTransactionManager
, которая выполнит Runnable
зарегистрирован в некотором хранилище ThreadLocal
после успешной фиксации (и удалите его из ThreadLocal
после отката). На самом деле, AbstractPlatformTransactionManager
имеет именно такую логику с TransactionSynchronization
s, но она слишком глубоко погружена в детали ее реализации.
Ответ 2
Это работает для меня:
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// things to do when commited
}
// other methods to override are available too
});
Ответ 3
Вы можете использовать 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);
}
}
}
}