Google guava singleton Eventbus多次发射

时间:2012-04-16 21:47:24

标签: java gwt guava vaadin

我使用vaadin + spring IOC + google guava eventbus。资源建议使用guava eventbus作为单例。但是当我这样做时,我有以下问题;

  • 我们说我在同一时间在3个不同的浏览器上运行应用程序,因此我有3个不同的应用程序实例。

  • 然后例如当我在一个浏览器上按下按钮并触发一个事件时,我注意到我的@subscribe注释的相关监听器方法被调用了3次!

这是我期望的正常行为,因为我使用eventbus作为单身人士吗?如果不是这里发生了什么? MainController是一个带有自定义Vaadin应用程序范围的Spring托管bean

class MainController{
  public MainController() throws DAOException, Exception {
    EventBusFactory.getEventBusInstance().register(this);
  }

  @Subscribe
  public void addFacetEvent(FacetAddedEvent event) throws DAOException {
    getTreeTableView().addToList(event.getData());
  }
} 

class EventBusFactory{
  public static EventBus getEventBusInstance() {
        if(eventBus==null){
            eventBus=new EventBus();
        }
        return eventBus;
    }
}

P.s我是否应该使用guava eventbus或guava gwt事件总线在Vaadin中犹豫不决?

由于

1 个答案:

答案 0 :(得分:3)

简短回答:此配置中的正常和预期行为(您有三个Vaadin应用程序,因此有三个MainController实例使用单个EventBus进行管理。)


通过自定义Vaadin应用范围您的意思是来自this Vaadin addon的范围吗?

无论如何,使用原型范围的MainController bean和Vaadin App重现你的情况很简单:

public class SandpitApplication extends Application {
  private static final long serialVersionUID = 1L;
  private static final Logger log = Logger.getLogger(SandpitApplication.class);

  // https://vaadin.com/wiki/-/wiki/Main/Spring%20Integration#section-Spring+Integration-SpringContextHelperClass
  private SpringContextHelper ctx;

  @Override
  public void init() {
    // vaadin stuff
    setTheme("common");
    final Window mainWindow = new Window("Vaadin Sample Application");
    setMainWindow(mainWindow);

    // get your bean from spring
    log.info("start SandpitApplication@" + Integer.toHexString(hashCode()));
    ctx = new SpringContextHelper(this);
    // create application-wide bean
    final MainController mainController = ctx.getBean("mainController");

    mainWindow.addComponent(new Button("click to post", new Button.ClickListener() {
      @Override public void buttonClick(final ClickEvent event) {
        log.info("click on button");
        EventBusFactory.getEventBusInstance().post(
            new FacetAddedEventImpl("click-"
                + new SimpleDateFormat("HH:mm:ss").format(new Date())));
        log.info(mainController);
      }
    }));
  }
}

MainController类:

class MainController {
  private static final Logger log = Logger.getLogger(MainController.class);

  public MainController() {
    log.info("creating MainController@" + Integer.toHexString(hashCode()));
    EventBusFactory.getEventBusInstance().register(this);
  }

  @Subscribe
  public void addFacetEvent(final FacetAddedEvent event) {
    final String signature = "MC@" + Integer.toHexString(hashCode()) + ": ";
    log.info("addFacetEvent in " + signature + event);
    // getTreeTableViewBuilder returns extended ArrayList with fancy add
    getTreeTableViewBuilder().addFacetToList(signature + event.getData());
  }

  // plus other stuff like toString etc.
}

执行以下操作时:

  1. 在浏览器中启动vaadin应用程序(App#1)。
  2. 单击App1#按钮。
  3. 启动另一个应用程序(App#2)。
  4. 单击App2#按钮
  5. 使用App#1返回标签页。
  6. 单击App1#按钮。
  7. 您将获得以下输出:

      

    启动SandpitApplication @ 75a5555a
      创建MainController @ 2e98f864
      点击按钮//#1
      MC @ 2e98f864中的addFacetEvent:FacetAddedEventImpl @ 6b527dc6 {data:click-13:42:45}
      MainController @ 2e98f864 {treeTableViewBuilder:[MC @ 2e98f864:click-13:42:45]}
      启动SandpitApplication @ 3f9e529
      创建MainController @ 2f8d604f
      点击按钮//#2
      MC @ 2e98f864中的addFacetEvent:FacetAddedEventImpl @ 36c1fc67 {data:click-13:42:47}
      MC @ 2f8d604f中的addFacetEvent:FacetAddedEventImpl @ 36c1fc67 {data:click-13:42:47}
      MainController @ 2f8d604f {treeTableViewBuilder:[MC @ 2f8d604f:click-13:42:47]}
      点击按钮//#1
      MC @ 2e98f864中的addFacetEvent:FacetAddedEventImpl @ 42d32028 {data:click-13:42:49}
      MC @ 2f8d604f中的addFacetEvent:FacetAddedEventImpl @ 42d32028 {data:click-13:42:49}
      MainController @ 2e98f864 {treeTableViewBuilder:[MC @ 2e98f864:click-13:42:45,MC @ 2e98f864:click-13:42:47,MC @ 2e98f864:click-13:42:49]}

    您现在应该看到单例EventBus正在管理两个应用程序范围的MainController bean,每个bean都在接收事件(因为它已由全局EventBus解析)。

    试图猜测你想要实现什么,我会说你需要创建应用程序范围的事件总线bean:

    <bean id="eventBus" class="com.google.common.eventbus.EventBus"
        scope="vaadinApplication" />
    

    关于P.S。:我们在Vaadin项目中广泛使用标准的Guava,不需要GWT版本。