从Guava EventBus订阅者处抛出异常

时间:2016-06-22 17:36:37

标签: java jpa guava event-bus

我正在使用Guava EventBus同步。如果任何订阅者抛出异常,我该如何回滚整个事务?如何抛出一个不会被EventBus订阅者捕获的异常?

3 个答案:

答案 0 :(得分:1)

您所要做的就是查看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
}

因此,从异常处理程序抛出的任何异常都无济于事。你有两个选择:

  1. 从您的订阅者方法中抛出错误(它太糟糕了)
  2. 或者从此流程中的任何位置手动将事务设置为仅回滚。我认为这类事情的最佳位置显然是EventBus异常处理程序。

答案 1 :(得分:0)

继承EventBus并创建自己的eventBus,它会抛出异常。 软件包必须是com.google.common.eventbus for 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);
    }
}

答案 2 :(得分:0)

我在发布商和订阅者之间使用java.lang.ThreadLocal变量解决了这个问题。

Publisher需要包装在一个读取线程局部异常并抛出它的类中

public void publish(Event event) {
  eventBus.post(event);
  if(threadLocalException != null) {
    Store threadLocalException in some variable say e
    Clear threadLocalException variable
    throw e;
  }
}

订阅者需要包装在类中以在线程局部变量

中设置异常
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);

}