#java #spring #spring-boot
Вопрос:
У меня есть приложение для загрузки spring, которое использует несколько @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
. Я заметил, что spring boot не регистрирует для них никаких исключений, когда они в конечном итоге создают исключение.
Из — за этого я хотел добавить некоторые общие средства ведения журнала для таких исключений. Я обнаружил, что TransactionalApplicationListener.SynchronizationCallback
это интерфейс, который мне нужно реализовать. Однако кажется сложным зарегистрировать эти обратные вызовы. Я не нашел ни одного вызова TransactionalApplicationListener#addCallback
в весенних зависимостях, который бы достиг этого.
Попытка получить список TransactionalApplicationListener
и SynchronizationCallback
введенных, а затем вызвать addCallback
a @PostConstruct
не продвинула меня дальше, потому что всегда не было введенных слушателей, хотя приложение успешно их использовало.
Итак, как мне добавить SynchronizationCallback
s в TransactionalApplicationListener
s во время запуска приложения spring boot?
Ответ №1:
Первое, что следует отметить, это то, что TransactionalApplicationListener
s, как и все ApplicationListener
, не являются бобами в контексте весны. Они живут несколько за его пределами (см. org.springframework.context.ConfigurableApplicationContext#addApplicationListener
). Таким образом, их введение невозможно в контексте приложения.
Во время отладки и просмотра источников spring можно обнаружить, что эти прослушиватели создаются org.springframework.transaction.event.TransactionalEventListenerFactory
. И именно в этом заключается мое решение. Мы украшаем эту фабрику другой, которая знает о SynchronizationCallback
:
public class SynchronizationCallbackAwareFactory implements EventListenerFactory, Ordered {
private final TransactionalEventListenerFactory delegate;
private final Provider<List<SynchronizationCallback>> synchronizationCallbacks;
private final int order;
public SynchronizationCallbackAwareFactory(TransactionalEventListenerFactory transactionalEventListenerFactory,
Provider<List<SynchronizationCallback>> synchronizationCallbacks,
int order) {
this.delegate = transactionalEventListenerFactory;
this.synchronizationCallbacks = synchronizationCallbacks;
this.order = order;
}
@Override
public boolean supportsMethod(Method method) {
return delegate.supportsMethod(method);
}
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
ApplicationListener<?> applicationListener = delegate.createApplicationListener(beanName, type, method);
if (applicationListener instanceof TransactionalApplicationListener) {
TransactionalApplicationListener<?> listener = (TransactionalApplicationListener<?>) applicationListener;
Collection<SynchronizationCallback> callbacks = this.synchronizationCallbacks.get();
callbacks.forEach(listener::addCallback);
}
return applicationListener;
}
@Override
public int getOrder() {
return order;
}
}
Обратите внимание, что javax.inject.Provider
в моем случае я использую a для извлечения обратных вызовов в самое позднее возможное время.
Декоратор должен быть Ordered
, потому что spring будет использовать первую фабрику, поддерживающую метод, который он использует. И поэтому порядок экземпляра этого класса должен иметь более высокий приоритет в качестве значения порядка 50
TransactionEventListenerFactory
.