Spring父子上下文和应用程序监听器

时间:2014-07-21 16:30:11

标签: java spring

我对Spring的ParentListener在Parent和Child Contexts方面的性质有疑问。让我们假设您创建一个父上下文,它创建一个单独的bean并注册为ApplicationListener。然后,使用父上下文创建子上下文。关闭Child Context Spring时会发出一个ContextClosedEvent。此事件是否会传播到父上下文,导致所有作为ApplicationListeners的父上下文单例接收事件?

我在文档中注意到[ContextClosedEvent] :( http://docs.spring.io/spring/docs/4.0.6.RELEASE/spring-framework-reference/htmlsingle/#context-functionality-events),"在ApplicationContext关闭时发布,使用ConfigurableApplicationContext接口上的close()方法。 "封闭"这意味着所有单例bean都被销毁。封闭的环境达到了生命的终点;它无法刷新或重新启动。"

基本上我要问的是Event Publishing仅限于特定的子上下文,还是在整个父/子上下文中传播?

2 个答案:

答案 0 :(得分:2)

调用所有侦听器,但参数(在本例中为ContextClosedEvent)将指向正在关闭的上下文。

以下测试创建父上下文,子上下文,启动它们,关闭父项,然后关闭子项。

public class ContextListenerTest {

    @Test
    public void contextListenerTest() {
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(ParentContext.class);
        AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext(ChildContext.class);
        child.setParent(parent);
        child.start();

        System.out.println("closing child now...");
        child.close();
        System.out.println("closing parent now...");
        parent.close();
    }

    public static class ParentContext {
        @Bean
        public ApplicationListener<ContextClosedEvent> closeEvent() {
            return new ApplicationListener<ContextClosedEvent>() {
                @Override
                public void onApplicationEvent(ContextClosedEvent event) {
                    System.out.println("parent listener: " + event);
                }
            };
        }
    }

    public static class ChildContext {
        @Bean
        public ApplicationListener<ContextClosedEvent> closeEvent() {
            return new ApplicationListener<ContextClosedEvent>() {
                @Override
                public void onApplicationEvent(ContextClosedEvent event) {
                    System.out.println("child listener: " + event);
                }
            };
        }
    }

}

提供的测试将输出以下文本:

closing child now...
child listener: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@3f1b7a14: startup date [Mon Jul 21 15:25:23 BRT 2014]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@94ac7e0]
parent listener: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@3f1b7a14: startup date [Mon Jul 21 15:25:23 BRT 2014]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@94ac7e0]
closing parent now...
parent listener: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@94ac7e0: startup date [Mon Jul 21 15:25:22 BRT 2014]; root of context hierarchy]

在第一个关闭(子)中,两个侦听器都被执行。但您可以使用event.getApplicationContext()来获取关闭的上下文。

答案 1 :(得分:1)

是的,Spring应用程序上下文确实将事件从子上下文传播到其所有祖先。

以下AbstractApplicationContext代码对此负责:

/**
 * Publish the given event to all listeners.
 * @param event the event to publish (may be an {@link ApplicationEvent}
 * or a payload object to be turned into a {@link PayloadApplicationEvent})
 * @param eventType the resolved event type, if known
 * @since 4.2
 */
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
        }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

正如您在publishMethod正文的结尾所看到的,如果有一个父项,那么任何事件也将传播给它。

相反,由于父母不了解孩子,因此父母的事件不会传播给孩子。

如果您不想传播事件,则可以简单地做到这一点(而不是将父级设置为子级上下文):

yourChildApplicationContext.addBeanFactoryPostProcessor(beanFactory -> {
  beanFactory.setParentBeanFactory(parentContext.getBeanFactory());
});

或者,如果您的子上下文是GenericApplicationContextAnnotationConfigApplicationContextGenericGroovyApplicationContextGenericXmlApplicationContextStaticApplicationContext),则可以进一步简化: >

yourChildApplicationContext.getDefaultListableBeanFactory()
   .setParentBeanFactory(parentContext.getBeanFactory());