Reuse ApplicationEventMulticaster
Spring Framework allows a custom `ApplicationEventMulticaster` bean to be defined with a well-defined bean. If such bean is present, it is used instead of the default implementation. This commit fixes `EventPublishingRunListener` to properly honour such arrangement. Rather than registering a `ApplicationEventMulticaster` to transmit the application listeners from the `SpringBootApplication` it now only uses an internal multicaster for early events (i.e. events that are fired before the context is actually refreshed). This has the positive effect of making sure that `ApplicationReadyEvent` is fired to the proper multicaster. Closes gh-6048
This commit is contained in:
parent
b968c2c7af
commit
1b0bbd89a2
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.boot.context.event;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringApplicationRunListener;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
@ -24,29 +23,32 @@ import org.springframework.context.ApplicationListener;
|
|||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
|
||||
*
|
||||
* <p>Uses an internal {@link ApplicationEventMulticaster} for the events that are
|
||||
* fired before the context is actually refreshed.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
|
||||
|
||||
private final ApplicationEventMulticaster multicaster;
|
||||
private final SpringApplication application;
|
||||
|
||||
private SpringApplication application;
|
||||
private final String[] args;
|
||||
|
||||
private String[] args;
|
||||
private final ApplicationEventMulticaster initialMulticaster;
|
||||
|
||||
public EventPublishingRunListener(SpringApplication application, String[] args) {
|
||||
this.application = application;
|
||||
this.args = args;
|
||||
this.multicaster = new SimpleApplicationEventMulticaster();
|
||||
this.initialMulticaster = new SimpleApplicationEventMulticaster();
|
||||
for (ApplicationListener<?> listener : application.getListeners()) {
|
||||
this.multicaster.addApplicationListener(listener);
|
||||
this.initialMulticaster.addApplicationListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,29 +59,19 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
|
|||
|
||||
@Override
|
||||
public void started() {
|
||||
publishEvent(new ApplicationStartedEvent(this.application, this.args));
|
||||
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(
|
||||
this.application, this.args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void environmentPrepared(ConfigurableEnvironment environment) {
|
||||
publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args,
|
||||
environment));
|
||||
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
|
||||
this.application, this.args, environment));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextPrepared(ConfigurableApplicationContext context) {
|
||||
registerApplicationEventMulticaster(context);
|
||||
}
|
||||
|
||||
private void registerApplicationEventMulticaster(
|
||||
ConfigurableApplicationContext context) {
|
||||
context.getBeanFactory().registerSingleton(
|
||||
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
|
||||
this.multicaster);
|
||||
if (this.multicaster instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.multicaster)
|
||||
.setBeanFactory(context.getBeanFactory());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,12 +82,15 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
|
|||
}
|
||||
context.addApplicationListener(listener);
|
||||
}
|
||||
publishEvent(new ApplicationPreparedEvent(this.application, this.args, context));
|
||||
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(
|
||||
this.application, this.args, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(ConfigurableApplicationContext context, Throwable exception) {
|
||||
publishEvent(getFinishedEvent(context, exception));
|
||||
// Listeners have been registered to the application context so we should
|
||||
// use it at this point
|
||||
context.publishEvent(getFinishedEvent(context, exception));
|
||||
}
|
||||
|
||||
private SpringApplicationEvent getFinishedEvent(
|
||||
|
@ -107,8 +102,4 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
|
|||
return new ApplicationReadyEvent(this.application, this.args, context);
|
||||
}
|
||||
|
||||
private void publishEvent(SpringApplicationEvent event) {
|
||||
this.multicaster.multicastEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,8 +56,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
|||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
|
@ -76,14 +78,17 @@ import org.springframework.util.StringUtils;
|
|||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.isA;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringApplication}.
|
||||
|
@ -672,7 +677,8 @@ public class SpringApplicationTests {
|
|||
|
||||
@Test
|
||||
public void registerListener() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class,
|
||||
ListenerConfig.class);
|
||||
application.setApplicationContextClass(SpyApplicationContext.class);
|
||||
final LinkedHashSet<ApplicationEvent> events = new LinkedHashSet<ApplicationEvent>();
|
||||
application.addListeners(new ApplicationListener<ApplicationEvent>() {
|
||||
|
@ -684,12 +690,18 @@ public class SpringApplicationTests {
|
|||
this.context = application.run();
|
||||
assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class);
|
||||
assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class);
|
||||
|
||||
ApplicationListener<ApplicationEvent> listener = this.context.getBean(
|
||||
"testApplicationListener", ApplicationListener.class);
|
||||
verify(listener).onApplicationEvent(argThat(isA(ContextRefreshedEvent.class)));
|
||||
verify(listener).onApplicationEvent(argThat(isA(ApplicationReadyEvent.class)));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerListenerWithCustomMulticaster() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class,
|
||||
Multicaster.class);
|
||||
ListenerConfig.class, Multicaster.class);
|
||||
application.setApplicationContextClass(SpyApplicationContext.class);
|
||||
final LinkedHashSet<ApplicationEvent> events = new LinkedHashSet<ApplicationEvent>();
|
||||
application.addListeners(new ApplicationListener<ApplicationEvent>() {
|
||||
|
@ -701,6 +713,12 @@ public class SpringApplicationTests {
|
|||
this.context = application.run();
|
||||
assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class);
|
||||
assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class);
|
||||
|
||||
ApplicationListener<ApplicationEvent> listener = this.context.getBean(
|
||||
"testApplicationListener", ApplicationListener.class);
|
||||
verify(listener).onApplicationEvent(argThat(isA(ContextRefreshedEvent.class)));
|
||||
verify(listener).onApplicationEvent(argThat(isA(ApplicationReadyEvent.class)));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -913,11 +931,21 @@ public class SpringApplicationTests {
|
|||
}
|
||||
|
||||
@Configuration
|
||||
static class Multicaster {
|
||||
static class ListenerConfig {
|
||||
|
||||
@Bean
|
||||
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
|
||||
return new SimpleApplicationEventMulticaster();
|
||||
public ApplicationListener<?> testApplicationListener() {
|
||||
return mock(ApplicationListener.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Multicaster {
|
||||
|
||||
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
|
||||
public ApplicationEventMulticaster applicationEventMulticaster() {
|
||||
return spy(new SimpleApplicationEventMulticaster());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue