diff --git a/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java b/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java index f3399926d23..867b9673b15 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java @@ -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. * + *

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); - } - } diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index a885077bf4e..2ebbad2f861 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -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 events = new LinkedHashSet(); application.addListeners(new ApplicationListener() { @@ -684,12 +690,18 @@ public class SpringApplicationTests { this.context = application.run(); assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class); assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class); + + ApplicationListener 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 events = new LinkedHashSet(); application.addListeners(new ApplicationListener() { @@ -701,6 +713,12 @@ public class SpringApplicationTests { this.context = application.run(); assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class); assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class); + + ApplicationListener 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()); } }