diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 58beb2324f..ce81686620 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -151,6 +151,8 @@ Application events are sent in the following order, as your application runs: the context is known, but before the context is created. . An `ApplicationPreparedEvent` is sent just before the refresh is started, but after bean definitions have been loaded. +. An `ApplicationReadyEvent` is sent after the refresh and any related callbacks have + been processed to indicate the application is ready to service requests. . An `ApplicationFailedEvent` is sent if there is an exception on startup. TIP: You often won't need to use application events, but it can be handy to know that they diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 49285a2824..22a0b6205d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -37,6 +37,7 @@ import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; @@ -138,6 +139,7 @@ import org.springframework.web.context.support.StandardServletEnvironment; * @author Dave Syer * @author Andy Wilkinson * @author Christian Dupuis + * @author Stephane Nicoll * @see #run(Object, String[]) * @see #run(Object[], String[]) * @see #SpringApplication(Object...) @@ -323,6 +325,7 @@ public class SpringApplication { runListener.finished(context, null); } + context.publishEvent(new ApplicationReadyEvent(context, args)); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted( diff --git a/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java new file mode 100644 index 0000000000..ab714bb063 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.context.event; + +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Event published as late as conceivably possible to indicate that the application is + * ready to service requests. The source of the event is the created + * {@link ConfigurableApplicationContext}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ApplicationReadyEvent extends ApplicationEvent { + + private final String[] args; + + /** + * @param applicationContext the main application context + * @param args the arguments the application is running with + */ + public ApplicationReadyEvent(ConfigurableApplicationContext applicationContext, String[] args) { + super(applicationContext); + this.args = args; + } + + public ConfigurableApplicationContext getApplicationContext() { + return (ConfigurableApplicationContext) getSource(); + } + + public String[] getArgs() { + return args; + } + +} 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 89e8eb57fe..4fbd929865 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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,9 +16,11 @@ package org.springframework.boot; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -33,7 +35,10 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextInitializer; @@ -85,6 +90,7 @@ import static org.mockito.Mockito.verify; * @author Dave Syer * @author Andy Wilkinson * @author Christian Dupuis + * @author Stephane Nicoll */ public class SpringApplicationTests { @@ -218,6 +224,22 @@ public class SpringApplicationTests { assertThat(getEnvironment().getProperty("foo"), equalTo("bar")); } + @Test + public void applicationRunningEventListener() { + SpringApplication application = new SpringApplication(ExampleConfig.class); + application.setWebEnvironment(false); + final AtomicReference reference = new AtomicReference(); + class ApplicationReadyEventListener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + reference.set(event.getApplicationContext()); + } + } + application.addListeners(new ApplicationReadyEventListener()); + this.context = application.run("--foo=bar"); + assertThat(this.context, sameInstance(reference.get())); + } + @Test public void contextRefreshedEventListener() throws Exception { SpringApplication application = new SpringApplication(ExampleConfig.class); @@ -236,6 +258,27 @@ public class SpringApplicationTests { assertThat(getEnvironment().getProperty("foo"), equalTo("bar")); } + @Test + public void eventsOrder() { + SpringApplication application = new SpringApplication(ExampleConfig.class); + application.setWebEnvironment(false); + final List events = new ArrayList(); + class ApplicationRunningEventListener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationEvent event) { + events.add((event)); + } + } + application.addListeners(new ApplicationRunningEventListener()); + this.context = application.run(); + assertThat(5, is(events.size())); + assertThat(events.get(0), is(instanceOf(ApplicationStartedEvent.class))); + assertThat(events.get(1), is(instanceOf(ApplicationEnvironmentPreparedEvent.class))); + assertThat(events.get(2), is(instanceOf(ApplicationPreparedEvent.class))); + assertThat(events.get(3), is(instanceOf(ContextRefreshedEvent.class))); + assertThat(events.get(4), is(instanceOf(ApplicationReadyEvent.class))); + } + @Test public void defaultApplicationContext() throws Exception { SpringApplication application = new SpringApplication(ExampleConfig.class);