活动只收到一个豆子? (尽管有几个)

时间:2016-06-13 21:15:40

标签: java spring

我对此感到困惑。我创建了三个应该监听事件的bean实例,但只有其中一个捕获事件。为什么?请参阅下面的代码和输出。 PS。春天来了。

事件处理程序类

package customevents.di;

import org.springframework.context.ApplicationListener;

public class CustomEventHandler implements ApplicationListener<CustomEvent> {

    private static int ID = 0;

    public CustomEventHandler() {
        ID++;
        System.out.println("Constructor called CustomEventHandler " + ID);

    }

    public void onApplicationEvent(CustomEvent event) {
        System.out.println(event.toString() + " " + ID); // Prints My Custom event and ID
    }

}

出版商

package customevents.di;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;

public class CustomEventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {

        this.publisher = publisher;
    }

    public void publish() {
        CustomEvent ce = new CustomEvent(this);
        publisher.publishEvent(ce);
    }
}

用法

  public static void main(String[] args) {
      ConfigurableApplicationContext context = 
      new ClassPathXmlApplicationContext("Beans.xml");

      CustomEventPublisher cvp = 
      (CustomEventPublisher) context.getBean("customEventPublisher");

      // Scope of these beans is prototype
      CustomEventHandler ce =  (CustomEventHandler) context.getBean("customEventHandler");
      CustomEventHandler ce1 =  (CustomEventHandler) context.getBean("customEventHandler");
      CustomEventHandler ce2 =  (CustomEventHandler) context.getBean("customEventHandler");

      cvp.publish();

   }

输出

Constructor called CustomEventHandler 1
Constructor called CustomEventHandler 2
Constructor called CustomEventHandler 3
Constructor called CustomEventHandler 4
My Custom Event 4

为什么只有一个bean收到了这个事件?

PS。按要求添加bean文件。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="customEventHandler" class="customevents.di.CustomEventHandler" scope="prototype">      
   </bean>

   <bean id="customEventPublisher" class="customevents.di.CustomEventPublisher">      
   </bean>

</beans>

5 个答案:

答案 0 :(得分:1)

UPDATE1:

Staphanee Nicollspring blog post中描述如下:

  

事件的实际处理将遵循您设置的语义   在你的豆子上。因此,如果您设置原型,我们将创建一个新的   在调用方法之前为你的bean的实例。我们是   为每个活动做这件事。如果您希望所有现有   要为特定事件调用的原型实例,事实并非如此   将要发生的事情(我们从未做过多播事件   基础设施并不意味着这样做。)

您错过了添加cvp.publish()来调用每个自定义事件处理程序。

costi-ciudatu

已经描述了另一个问题static部分

希望它能奏效。

要了解更多信息,您可以阅读本教程:Custom events in spring

Github资源链接:

Spring app event demo

答案 1 :(得分:1)

您面临的问题似乎是弹簧的限制,至少在某些旧版本中,应该已经修复。有些用户报告说它仍然坏了,所以如果你使用的是一个应该有效的弹簧版本,你可能需要提交一张票。

我找到了以下门票:

答案 2 :(得分:1)

Spring将在上下文启动时注册侦听器。因此,它将在事件触发时创建bean的新实例。

但您也可以在运行时注册侦听器实例( context.addApplicationListener(ce); ),使用如下内容:

public static void main(String[] args) {
    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

    CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher");

    // Scope of these beans is prototype
    CustomEventHandler ce = (CustomEventHandler) context.getBean("customEventHandler");
    context.addApplicationListener(ce);
    CustomEventHandler ce1 = (CustomEventHandler) context.getBean("customEventHandler");
    context.addApplicationListener(ce1);
    CustomEventHandler ce2 = (CustomEventHandler) context.getBean("customEventHandler");
    context.addApplicationListener(ce2);

    cvp.publish();

}    

答案 3 :(得分:0)

这里的关键是ApplicationListener实例在ApplicationContext启动期间注册(请查看ApplicationEventMulticaster及其实现)。

在您的情况下,这意味着已注册的ApplicationListener实例实际上是customEventHandler中定义了标识为Beans.xml的原型作用域bean,它是处理您发布的事件的那个。< / p>

您应该做的是从应用程序上下文中获取ApplicationEventMulticaster并使用它来注册在运行时创建的bean。

答案 4 :(得分:0)

确实只有第四个bean被通知了。但是最后一个豆是你自己没有创造的(因为你只召唤了getBean三次)。

如果从代码中删除cvp.publish()行,您将看到只创建了三个bean。

第4个实际上是在事件调度期间创建的。行为由AbstractApplicationEventMulticaster中的代码解释。它只存储实现ApplicationListener的bean名称,然后在需要通知侦听器时执行BeanFactory.getBean()

以下是相关代码(方法getApplicationListeners(),版本4.0.3.RELEASE):

for (String listenerBeanName : listenerBeans) {
    try {
        Class<?> listenerType = beanFactory.getType(listenerBeanName);
        if (listenerType == null || supportsEvent(listenerType, event)) {
            ApplicationListener<?> listener =
                    beanFactory.getBean(listenerBeanName, ApplicationListener.class);
            if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                retriever.applicationListenerBeans.add(listenerBeanName);
                            allListeners.add(listener);
            }
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
    }
}

如果您认为prototype bean毕竟只是一个&#34;原型&#34;那么这是有意义的。 /&#34;模板&#34;。容器没有为创建的实际实例保留引用。它会在需要时调用getBean(),这将导致一个新实例。