Я использую Guava EventBus в синхронизации. Как я могу отменить полную транзакцию, если какой-либо из подписчиков выбрал исключение? Как я могу выбросить исключение, которое не будет захвачено подписчиком EventBus?Throw исключение из Guava EventBus подписчик
ответ
Я решил его с помощью java.lang.ThreadLocal
переменных через издатель и абонент.
Издатель должен быть обернут в классе, который читает нить локальное исключение и бросает его нужно завернуть в классе
public void publish(Event event) {
eventBus.post(event);
if(threadLocalException != null) {
Store threadLocalException in some variable say e
Clear threadLocalException variable
throw e;
}
}
Subscriber установить исключение в потоке локальной переменной
public abstract class EventSubscriber<T extends Event> {
@Subscribe
public void invoke(T event) {
try {
handle(event);
} catch (Exception e) {
Set thread local variable to e
}
}
protected abstract void handle(T event);
}
Все, что вам нужно сделать, это посмотреть исходный код класса Guava EventBus
.
Начнем с конца:
Как я могу выбросить исключение, которое не будет перехватывается EventBus подписчика?
Методы подписчиков вызываются последовательно, один за другим, методом com.google.common.eventbus.Dispatcher#dispatch
. Чтобы вызвать методы ваших подписчиков, EventBus использует метод отражения Method#invoke
, который, в свою очередь, выдает InvocationTargetException
, если вызываемый метод выдает исключение.
Как вы можете видеть, InvocationTargetException
(который будет обернут вокруг Exception
) обрабатывается следующим образом:
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
на верхнем уровне, исключение обрабатывается так:
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
TL; DR
Таким образом, единственный способ пропустить EventBus
обработчик исключений заключается в том, чтобы бросить не Exception
, но Error
в вашем подписном методе - что, безусловно, является плохой практикой.
Как отменить полную транзакцию, если какой-либо из абонентов выбрасывает исключение?
EventBus
исключение обработчик обрабатывает исключения по телефону com.google.common.eventbus.EventBus#handleSubscriberException
способ. Это выглядит так:
try {
exceptionHandler.handleException(e, context);
} catch (Throwable e2) {
// logging
}
Таким образом, любые исключения, брошенные из обработчика исключений, не помогут. У вас есть два варианта:
- Либо бросок ошибки от вашего метода абонента (это ооочень плохо)
- Или вручную установить транзакцию как откат только из любого места в этом потоке. Я думаю, что лучшим местом для таких вещей является, очевидно,
EventBus
обработчик исключений.
Наследовать EventBus и создать собственный EventBus, который будет генерировать исключение. Пакет должен быть com.google.common.eventbus для handleSubscriberException является внутренним методом.
package com.google.common.eventbus;
import com.google.common.util.concurrent.MoreExecutors;
/**
* A eventbus wihch will throw exceptions during event handle process.
* @author ytm
*
*/
public class ErrorThrowEventBus extends EventBus {
/**
* Creates a new EventBus with the given {@code identifier}.
*
* @param identifier a brief name for this bus, for logging purposes. Should be a valid Java
* identifier.
*/
public ErrorThrowEventBus(String identifier) {
super(
identifier,
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
LoggingHandler.INSTANCE);
}
/**
* Creates a new EventBus with the given {@link SubscriberExceptionHandler}.
*
* @param exceptionHandler Handler for subscriber exceptions.
* @since 16.0
*/
public ErrorThrowEventBus(SubscriberExceptionHandler exceptionHandler) {
super(
"default",
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
exceptionHandler);
}
/**
* Just throw a EventHandleException if there's any exception.
* @param e
* @param context
* @throws EventHandleException
*/
@Override
void handleSubscriberException(Throwable e, SubscriberExceptionContext context) throws EventHandleException {
throw new EventHandleException(e);
}
}
I Если бы это было возможно, удивляйтесь. –
Если это невозможно, разве это не ограничение шины событий, что она не может быть транзакционной? – Shikhar
Да. Конечно. –