Как добавить обратные вызовы синхронизации в @TransactionalEventListener во время запуска приложения spring boot?

#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 .