我对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仅限于特定的子上下文,还是在整个父/子上下文中传播?
答案 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());
});
或者,如果您的子上下文是GenericApplicationContext
(AnnotationConfigApplicationContext
,GenericGroovyApplicationContext
,GenericXmlApplicationContext
,StaticApplicationContext
),则可以进一步简化: >
yourChildApplicationContext.getDefaultListableBeanFactory()
.setParentBeanFactory(parentContext.getBeanFactory());