#events #jakarta-ee #cdi
Вопрос:
При аннотации метода наблюдателя AFTER_SUCCESS
события принимаются в обратном порядке , в котором они фактически были запущены во время транзакции.
Пример псевдокода:
@Transactional
void test(){
pushEvent.fire( new PushEvent(10) );
pushEvent.fire( new PushEvent(20) );
pushEvent.fire( new PushEvent(30) );
}
Наблюдатель:
void onPushEvent( @Observes( during = TransactionPhase.AFTER_SUCCESS ) PushEvent event ){
System.out.println(event.getValue())
}
Неожиданный, но наблюдаемый результат:
30
20
10
Можно ли это изменить?
Комментарии:
1. Наблюдатели в фазе AFTER_xxx вызываются несинхронизированно (не асинхронно, т. е. в другом потоке, просто позже). В спецификации ничего не говорится о порядке обработки событий в этом случае, поэтому я предполагаю, что порядок зависит от реализации, и вы не можете полагаться на него! Я бы подумал о том, чтобы поместить человека
PushEvents
в aList
и запустить событие, содержащее этот список, чтобы обеспечить правильный порядок обработки.2. В качестве альтернативы, например, если события не запускаются из одного места, у вас может быть синхронный наблюдатель, собирающий человека
PushEvents
вList
(например, в@RequestScoped
бобе). Это гарантированно будет вызвано в нужном порядке. Затем каким-то образом этот синхронный наблюдатель запускает событие, содержащее список, для обработки на этапе AFTER_SUCCESS.
Ответ №1:
Исправлено это в моем проекте с помощью локального буфера событий потока, который сбрасывается при AFTER_SUCCESS и воспроизводит события в том порядке, в котором они появились в первую очередь
ThreadLocal<List<PushEvent>> threadEventBufferHolder = ThreadLocal.withInitial( ArrayList::new );
void onPushEvent( @Observes( during = TransactionPhase.IN_PROGRESS ) PushEvent event ){
threadEventBufferHolder.get().add( event );
}
void onPushEventFailure( @Observes( during = TransactionPhase.AFTER_FAILURE ) PushEvent event ){
buffer.clear();
}
void onPushEventCommit( @Observes( during = TransactionPhase.AFTER_SUCCESS ) PushEvent event ){
List<PushEvent> buffer = threadEventBufferHolder.get();
buffer.forEach( this::doPrintlnValue );
buffer.clear();
}
Несвязанный код удален для удобства чтения