Skip to content

Commit 7b2b119

Browse files
committed
Add ApplicationReadyEvent
Add an event that indicates the Spring Application has fully started and is now ready to service requests. While ContextRefreshEvent provides such hook for a regular spring application, this dedicated event is triggered once all callbacks have been processed and right before the context is returned to the caller. Besides, such event is triggered once per application, regardless of the number of (child) contexts that could have been created. Closes spring-projectsgh-2638
1 parent bfee98e commit 7b2b119

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ Application events are sent in the following order, as your application runs:
151151
the context is known, but before the context is created.
152152
. An `ApplicationPreparedEvent` is sent just before the refresh is started, but after bean
153153
definitions have been loaded.
154+
. An `ApplicationReadyEvent` is sent after the refresh and any related callbacks have
155+
been processed to indicate the application is ready to service requests.
154156
. An `ApplicationFailedEvent` is sent if there is an exception on startup.
155157

156158
TIP: You often won't need to use application events, but it can be handy to know that they

spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3838
import org.springframework.beans.factory.support.BeanNameGenerator;
3939
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
40+
import org.springframework.boot.context.event.ApplicationReadyEvent;
4041
import org.springframework.context.ApplicationContext;
4142
import org.springframework.context.ApplicationContextInitializer;
4243
import org.springframework.context.ApplicationListener;
@@ -138,6 +139,7 @@
138139
* @author Dave Syer
139140
* @author Andy Wilkinson
140141
* @author Christian Dupuis
142+
* @author Stephane Nicoll
141143
* @see #run(Object, String[])
142144
* @see #run(Object[], String[])
143145
* @see #SpringApplication(Object...)
@@ -323,6 +325,7 @@ public ConfigurableApplicationContext run(String... args) {
323325
runListener.finished(context, null);
324326
}
325327

328+
context.publishEvent(new ApplicationReadyEvent(context, args));
326329
stopWatch.stop();
327330
if (this.logStartupInfo) {
328331
new StartupInfoLogger(this.mainApplicationClass).logStarted(
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.context.event;
18+
19+
import org.springframework.context.ApplicationEvent;
20+
import org.springframework.context.ConfigurableApplicationContext;
21+
22+
/**
23+
* Event published as late as conceivably possible to indicate that the application is
24+
* ready to service requests. The source of the event is the created
25+
* {@link ConfigurableApplicationContext}.
26+
*
27+
* @author Stephane Nicoll
28+
* @since 1.3.0
29+
*/
30+
@SuppressWarnings("serial")
31+
public class ApplicationReadyEvent extends ApplicationEvent {
32+
33+
private final String[] args;
34+
35+
/**
36+
* @param applicationContext the main application context
37+
* @param args the arguments the application is running with
38+
*/
39+
public ApplicationReadyEvent(ConfigurableApplicationContext applicationContext, String[] args) {
40+
super(applicationContext);
41+
this.args = args;
42+
}
43+
44+
public ConfigurableApplicationContext getApplicationContext() {
45+
return (ConfigurableApplicationContext) getSource();
46+
}
47+
48+
public String[] getArgs() {
49+
return args;
50+
}
51+
52+
}

spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2014 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@
1616

1717
package org.springframework.boot;
1818

19+
import java.util.ArrayList;
1920
import java.util.Arrays;
2021
import java.util.Collections;
2122
import java.util.LinkedHashSet;
23+
import java.util.List;
2224
import java.util.Set;
2325
import java.util.concurrent.atomic.AtomicReference;
2426

@@ -33,7 +35,10 @@
3335
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
3436
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
3537
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
38+
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
3639
import org.springframework.boot.context.event.ApplicationPreparedEvent;
40+
import org.springframework.boot.context.event.ApplicationReadyEvent;
41+
import org.springframework.boot.context.event.ApplicationStartedEvent;
3742
import org.springframework.context.ApplicationContext;
3843
import org.springframework.context.ApplicationContextAware;
3944
import org.springframework.context.ApplicationContextInitializer;
@@ -85,6 +90,7 @@
8590
* @author Dave Syer
8691
* @author Andy Wilkinson
8792
* @author Christian Dupuis
93+
* @author Stephane Nicoll
8894
*/
8995
public class SpringApplicationTests {
9096

@@ -218,6 +224,22 @@ public void initialize(ConfigurableApplicationContext context) {
218224
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
219225
}
220226

227+
@Test
228+
public void applicationRunningEventListener() {
229+
SpringApplication application = new SpringApplication(ExampleConfig.class);
230+
application.setWebEnvironment(false);
231+
final AtomicReference<ApplicationContext> reference = new AtomicReference<ApplicationContext>();
232+
class ApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
233+
@Override
234+
public void onApplicationEvent(ApplicationReadyEvent event) {
235+
reference.set(event.getApplicationContext());
236+
}
237+
}
238+
application.addListeners(new ApplicationReadyEventListener());
239+
this.context = application.run("--foo=bar");
240+
assertThat(this.context, sameInstance(reference.get()));
241+
}
242+
221243
@Test
222244
public void contextRefreshedEventListener() throws Exception {
223245
SpringApplication application = new SpringApplication(ExampleConfig.class);
@@ -236,6 +258,27 @@ public void onApplicationEvent(ContextRefreshedEvent event) {
236258
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
237259
}
238260

261+
@Test
262+
public void eventsOrder() {
263+
SpringApplication application = new SpringApplication(ExampleConfig.class);
264+
application.setWebEnvironment(false);
265+
final List<ApplicationEvent> events = new ArrayList<ApplicationEvent>();
266+
class ApplicationRunningEventListener implements ApplicationListener<ApplicationEvent> {
267+
@Override
268+
public void onApplicationEvent(ApplicationEvent event) {
269+
events.add((event));
270+
}
271+
}
272+
application.addListeners(new ApplicationRunningEventListener());
273+
this.context = application.run();
274+
assertThat(5, is(events.size()));
275+
assertThat(events.get(0), is(instanceOf(ApplicationStartedEvent.class)));
276+
assertThat(events.get(1), is(instanceOf(ApplicationEnvironmentPreparedEvent.class)));
277+
assertThat(events.get(2), is(instanceOf(ApplicationPreparedEvent.class)));
278+
assertThat(events.get(3), is(instanceOf(ContextRefreshedEvent.class)));
279+
assertThat(events.get(4), is(instanceOf(ApplicationReadyEvent.class)));
280+
}
281+
239282
@Test
240283
public void defaultApplicationContext() throws Exception {
241284
SpringApplication application = new SpringApplication(ExampleConfig.class);

0 commit comments

Comments
 (0)