0
# Context Lifecycle
1
2
Spring Boot provides a comprehensive application context lifecycle with events for monitoring startup, shutdown, and availability states. This includes application lifecycle events, availability probes, and failure analysis.
3
4
## Capabilities
5
6
### Application Lifecycle Events
7
8
Events published during different phases of the Spring Boot application lifecycle.
9
10
```java { .api }
11
/**
12
* Event published as early as conceivably possible as soon as a SpringApplication
13
* has been started - before the Environment or ApplicationContext is available
14
*/
15
public class ApplicationStartingEvent extends SpringApplicationEvent {
16
17
/**
18
* Create a new ApplicationStartingEvent instance
19
* @param application the current application
20
* @param args the arguments the application is running with
21
*/
22
public ApplicationStartingEvent(SpringApplication application, String[] args);
23
24
/**
25
* Return the SpringApplication
26
* @return the application
27
*/
28
public SpringApplication getSpringApplication();
29
30
/**
31
* Return the application arguments
32
* @return the arguments
33
*/
34
public String[] getArgs();
35
}
36
37
/**
38
* Event published when a SpringApplication is starting up and the Environment is
39
* first available for inspection and modification
40
*/
41
public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent {
42
43
/**
44
* Create a new ApplicationEnvironmentPreparedEvent instance
45
* @param application the current application
46
* @param args the arguments the application is running with
47
* @param environment the environment that was just prepared
48
*/
49
public ApplicationEnvironmentPreparedEvent(SpringApplication application, String[] args,
50
ConfigurableEnvironment environment);
51
52
/**
53
* Return the environment
54
* @return the environment
55
*/
56
public ConfigurableEnvironment getEnvironment();
57
}
58
59
/**
60
* Event published once the ApplicationContext has been created and prepared, but
61
* before sources have been loaded
62
*/
63
public class ApplicationContextInitializedEvent extends SpringApplicationEvent {
64
65
/**
66
* Create a new ApplicationContextInitializedEvent instance
67
* @param application the current application
68
* @param args the arguments the application is running with
69
* @param context the context that has been initialized
70
*/
71
public ApplicationContextInitializedEvent(SpringApplication application, String[] args,
72
ConfigurableApplicationContext context);
73
74
/**
75
* Return the application context
76
* @return the application context
77
*/
78
public ConfigurableApplicationContext getApplicationContext();
79
}
80
81
/**
82
* Event published as late as conceivably possible to indicate that the application
83
* is ready to service requests. The source of the event is the SpringApplication itself
84
*/
85
public class ApplicationPreparedEvent extends SpringApplicationEvent {
86
87
/**
88
* Create a new ApplicationPreparedEvent instance
89
* @param application the current application
90
* @param args the arguments the application is running with
91
* @param context the ApplicationContext about to be refreshed
92
*/
93
public ApplicationPreparedEvent(SpringApplication application, String[] args,
94
ConfigurableApplicationContext context);
95
96
/**
97
* Return the application context
98
* @return the application context
99
*/
100
public ConfigurableApplicationContext getApplicationContext();
101
}
102
103
/**
104
* Event published once the application context has been refreshed but before any
105
* application and command line runners have been called
106
*/
107
public class ApplicationStartedEvent extends SpringApplicationEvent {
108
109
/**
110
* Create a new ApplicationStartedEvent instance
111
* @param application the current application
112
* @param args the arguments the application is running with
113
* @param context the ApplicationContext that was started
114
* @param timeTaken the time taken to start the application or null if unknown
115
*/
116
public ApplicationStartedEvent(SpringApplication application, String[] args,
117
ConfigurableApplicationContext context, Duration timeTaken);
118
119
/**
120
* Return the application context
121
* @return the application context
122
*/
123
public ConfigurableApplicationContext getApplicationContext();
124
125
/**
126
* Return the time taken for the application to be ready or null if unknown
127
* @return the startup time
128
*/
129
public Duration getTimeTaken();
130
}
131
132
/**
133
* Event published as late as conceivably possible to indicate that the application
134
* is ready to service requests. The source of the event is the SpringApplication itself
135
*/
136
public class ApplicationReadyEvent extends SpringApplicationEvent {
137
138
/**
139
* Create a new ApplicationReadyEvent instance
140
* @param application the current application
141
* @param args the arguments the application is running with
142
* @param context the ApplicationContext that is ready
143
* @param timeTaken the time taken for the application to be ready or null if unknown
144
*/
145
public ApplicationReadyEvent(SpringApplication application, String[] args,
146
ConfigurableApplicationContext context, Duration timeTaken);
147
148
/**
149
* Return the application context
150
* @return the application context
151
*/
152
public ConfigurableApplicationContext getApplicationContext();
153
154
/**
155
* Return the time taken for the application to be ready or null if unknown
156
* @return the startup time
157
*/
158
public Duration getTimeTaken();
159
}
160
161
/**
162
* Event published when a SpringApplication fails to start
163
*/
164
public class ApplicationFailedEvent extends SpringApplicationEvent {
165
166
/**
167
* Create a new ApplicationFailedEvent instance
168
* @param application the current application
169
* @param args the arguments the application was running with
170
* @param context the context that was being created (may be null)
171
* @param exception the exception that caused the failure
172
*/
173
public ApplicationFailedEvent(SpringApplication application, String[] args,
174
ConfigurableApplicationContext context, Throwable exception);
175
176
/**
177
* Return the application context
178
* @return the application context
179
*/
180
public ConfigurableApplicationContext getApplicationContext();
181
182
/**
183
* Return the exception that caused the failure
184
* @return the exception
185
*/
186
public Throwable getException();
187
}
188
```
189
190
**Usage Examples:**
191
192
```java
193
@Component
194
public class ApplicationLifecycleListener {
195
196
private static final Logger logger = LoggerFactory.getLogger(ApplicationLifecycleListener.class);
197
198
@EventListener
199
public void handleApplicationStarting(ApplicationStartingEvent event) {
200
logger.info("Application starting with args: {}", Arrays.toString(event.getArgs()));
201
}
202
203
@EventListener
204
public void handleEnvironmentPrepared(ApplicationEnvironmentPreparedEvent event) {
205
ConfigurableEnvironment env = event.getEnvironment();
206
logger.info("Environment prepared. Active profiles: {}",
207
Arrays.toString(env.getActiveProfiles()));
208
}
209
210
@EventListener
211
public void handleContextInitialized(ApplicationContextInitializedEvent event) {
212
logger.info("Application context initialized: {}",
213
event.getApplicationContext().getClass().getSimpleName());
214
}
215
216
@EventListener
217
public void handleApplicationPrepared(ApplicationPreparedEvent event) {
218
ConfigurableApplicationContext context = event.getApplicationContext();
219
logger.info("Application prepared. Bean count: {}", context.getBeanDefinitionCount());
220
}
221
222
@EventListener
223
public void handleApplicationStarted(ApplicationStartedEvent event) {
224
Duration timeTaken = event.getTimeTaken();
225
logger.info("Application started successfully in {} seconds",
226
timeTaken != null ? timeTaken.toSeconds() : "unknown");
227
}
228
229
@EventListener
230
public void handleApplicationReady(ApplicationReadyEvent event) {
231
Duration timeTaken = event.getTimeTaken();
232
logger.info("Application ready to service requests in {} seconds",
233
timeTaken != null ? timeTaken.toSeconds() : "unknown");
234
}
235
236
@EventListener
237
public void handleApplicationFailed(ApplicationFailedEvent event) {
238
Throwable exception = event.getException();
239
logger.error("Application failed to start", exception);
240
}
241
}
242
```
243
244
### Application Availability
245
246
Application availability states for health monitoring and readiness probes.
247
248
```java { .api }
249
/**
250
* Provides availability information for the application
251
*/
252
public interface ApplicationAvailability {
253
254
/**
255
* Return the AvailabilityState of the application for the given StateType
256
* @param stateType the state type
257
* @return the availability state (never null)
258
*/
259
<S extends AvailabilityState> S getState(Class<S> stateType);
260
261
/**
262
* Return the AvailabilityState of the application for the given StateType
263
* @param stateType the state type
264
* @param defaultState the default state to return if no state of the given
265
* type has been published yet
266
* @return the availability state (never null)
267
*/
268
<S extends AvailabilityState> S getState(Class<S> stateType, S defaultState);
269
270
/**
271
* Return the last AvailabilityChangeEvent received for a given state type
272
* @param stateType the state type
273
* @return the last change event or null
274
*/
275
<S extends AvailabilityState> AvailabilityChangeEvent<S> getLastChangeEvent(Class<S> stateType);
276
}
277
278
/**
279
* "Liveness" state of the application
280
*/
281
public enum LivenessState implements AvailabilityState {
282
283
/**
284
* The application is running and its internal state is correct
285
*/
286
CORRECT,
287
288
/**
289
* The application is running but its internal state is broken
290
*/
291
BROKEN
292
}
293
294
/**
295
* "Readiness" state of the application
296
*/
297
public enum ReadinessState implements AvailabilityState {
298
299
/**
300
* The application is ready to receive traffic
301
*/
302
ACCEPTING_TRAFFIC,
303
304
/**
305
* The application is not willing to receive traffic
306
*/
307
REFUSING_TRAFFIC
308
}
309
310
/**
311
* Base interface for availability states
312
*/
313
public interface AvailabilityState {
314
}
315
316
/**
317
* Application event fired when the AvailabilityState of the application changes
318
*/
319
public class AvailabilityChangeEvent<S extends AvailabilityState> extends ApplicationEvent {
320
321
/**
322
* Create a new AvailabilityChangeEvent instance
323
* @param source the source of the event
324
* @param state the availability state
325
*/
326
public AvailabilityChangeEvent(Object source, S state);
327
328
/**
329
* Return the new availability state
330
* @return the availability state
331
*/
332
public S getState();
333
334
/**
335
* Convenience method that can be used to publish an AvailabilityChangeEvent to the
336
* given application context
337
* @param context the context to publish the event to
338
* @param state the changed state
339
*/
340
public static <S extends AvailabilityState> void publish(ApplicationContext context, S state);
341
342
/**
343
* Convenience method that can be used to publish an AvailabilityChangeEvent to the
344
* given application context
345
* @param context the context to publish the event to
346
* @param source the source of the event
347
* @param state the changed state
348
*/
349
public static <S extends AvailabilityState> void publish(ApplicationContext context, Object source, S state);
350
}
351
```
352
353
**Usage Examples:**
354
355
```java
356
@Component
357
public class HealthMonitor {
358
359
@Autowired
360
private ApplicationAvailability applicationAvailability;
361
362
@Autowired
363
private ApplicationContext applicationContext;
364
365
@EventListener
366
public void handleAvailabilityChange(AvailabilityChangeEvent<?> event) {
367
AvailabilityState state = event.getState();
368
System.out.println("Availability changed to: " + state);
369
370
if (state == LivenessState.BROKEN) {
371
// Log critical error, notify monitoring systems
372
System.err.println("Application liveness is BROKEN!");
373
}
374
375
if (state == ReadinessState.REFUSING_TRAFFIC) {
376
// Application is not ready to handle requests
377
System.out.println("Application is not ready for traffic");
378
}
379
}
380
381
public void checkCurrentState() {
382
LivenessState liveness = applicationAvailability.getState(LivenessState.class);
383
ReadinessState readiness = applicationAvailability.getState(ReadinessState.class);
384
385
System.out.println("Current liveness: " + liveness);
386
System.out.println("Current readiness: " + readiness);
387
}
388
389
public void markApplicationAsReady() {
390
AvailabilityChangeEvent.publish(applicationContext, ReadinessState.ACCEPTING_TRAFFIC);
391
}
392
393
public void markApplicationAsBroken(Exception cause) {
394
System.err.println("Marking application as broken due to: " + cause.getMessage());
395
AvailabilityChangeEvent.publish(applicationContext, this, LivenessState.BROKEN);
396
}
397
}
398
399
// Custom availability indicator
400
@Component
401
public class DatabaseHealthIndicator implements ApplicationListener<ContextRefreshedEvent> {
402
403
@Autowired
404
private ApplicationContext applicationContext;
405
406
@Autowired
407
private DataSource dataSource;
408
409
@Override
410
public void onApplicationEvent(ContextRefreshedEvent event) {
411
// Check database connectivity on startup
412
try {
413
Connection connection = dataSource.getConnection();
414
connection.close();
415
// Database is healthy - application can accept traffic
416
AvailabilityChangeEvent.publish(applicationContext, ReadinessState.ACCEPTING_TRAFFIC);
417
} catch (SQLException e) {
418
// Database is unhealthy - refuse traffic
419
AvailabilityChangeEvent.publish(applicationContext, ReadinessState.REFUSING_TRAFFIC);
420
}
421
}
422
423
@Scheduled(fixedRate = 30000) // Check every 30 seconds
424
public void healthCheck() {
425
try {
426
Connection connection = dataSource.getConnection();
427
// Perform a simple query
428
PreparedStatement stmt = connection.prepareStatement("SELECT 1");
429
ResultSet rs = stmt.executeQuery();
430
rs.close();
431
stmt.close();
432
connection.close();
433
434
// Database is responsive
435
AvailabilityChangeEvent.publish(applicationContext, LivenessState.CORRECT);
436
} catch (SQLException e) {
437
// Database connection failed
438
AvailabilityChangeEvent.publish(applicationContext, LivenessState.BROKEN);
439
}
440
}
441
}
442
```
443
444
### Failure Analysis
445
446
System for analyzing and providing human-readable descriptions of application startup failures.
447
448
```java { .api }
449
/**
450
* A FailureAnalyzer is used to analyze a failure and provide diagnostic information
451
* that can be displayed to the user
452
*/
453
@FunctionalInterface
454
public interface FailureAnalyzer {
455
456
/**
457
* Returns an analysis of the given failure, or null if no analysis was possible
458
* @param failure the failure
459
* @return the analysis or null
460
*/
461
FailureAnalysis analyze(Throwable failure);
462
}
463
464
/**
465
* The result of performing failure analysis
466
*/
467
public class FailureAnalysis {
468
469
private final String description;
470
private final String action;
471
private final Throwable cause;
472
473
/**
474
* Creates a new FailureAnalysis with the given description and action
475
* @param description the description
476
* @param action the action
477
* @param cause the cause of the failure
478
*/
479
public FailureAnalysis(String description, String action, Throwable cause);
480
481
/**
482
* Returns a description of the failure
483
* @return the description
484
*/
485
public String getDescription();
486
487
/**
488
* Returns the action that the user should take to address the failure
489
* @return the action
490
*/
491
public String getAction();
492
493
/**
494
* Returns the cause of the failure
495
* @return the cause
496
*/
497
public Throwable getCause();
498
}
499
500
/**
501
* Abstract base class for most FailureAnalyzer implementations
502
*/
503
public abstract class AbstractFailureAnalyzer<T extends Throwable> implements FailureAnalyzer {
504
505
@Override
506
public final FailureAnalysis analyze(Throwable failure) {
507
T cause = findCause(failure, getCauseType());
508
if (cause != null) {
509
return analyze(failure, cause);
510
}
511
return null;
512
}
513
514
/**
515
* Returns the cause type that this analyzer handles
516
* @return the cause type
517
*/
518
protected abstract Class<T> getCauseType();
519
520
/**
521
* Returns an analysis of the given failure, or null if no analysis was possible
522
* @param rootFailure the root failure passed to the analyzer
523
* @param cause the actual found cause
524
* @return the analysis or null
525
*/
526
protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause);
527
528
/**
529
* Find the cause of the specified type in the given failure
530
* @param failure the source failure
531
* @param type the type to find
532
* @return the cause or null
533
*/
534
protected final <E extends Throwable> E findCause(Throwable failure, Class<E> type) {
535
// Implementation details
536
return null;
537
}
538
}
539
```
540
541
**Usage Examples:**
542
543
```java
544
// Custom failure analyzer for database connection issues
545
@Component
546
public class DatabaseConnectionFailureAnalyzer extends AbstractFailureAnalyzer<SQLException> {
547
548
@Override
549
protected Class<SQLException> getCauseType() {
550
return SQLException.class;
551
}
552
553
@Override
554
protected FailureAnalysis analyze(Throwable rootFailure, SQLException cause) {
555
String description = String.format(
556
"Failed to connect to database: %s (Error code: %d)",
557
cause.getMessage(),
558
cause.getErrorCode()
559
);
560
561
String action = buildActionMessage(cause);
562
563
return new FailureAnalysis(description, action, cause);
564
}
565
566
private String buildActionMessage(SQLException cause) {
567
switch (cause.getErrorCode()) {
568
case 1045: // Access denied
569
return "Check your database credentials in application.properties:\n" +
570
"spring.datasource.username=<username>\n" +
571
"spring.datasource.password=<password>";
572
case 1049: // Unknown database
573
return "Verify that the database exists and the name is correct:\n" +
574
"spring.datasource.url=jdbc:mysql://localhost:3306/<database-name>";
575
default:
576
return "Verify your database configuration and ensure the database server is running.";
577
}
578
}
579
}
580
581
// Custom failure analyzer for missing properties
582
@Component
583
public class ConfigurationPropertyFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
584
585
@Override
586
protected Class<BindException> getCauseType() {
587
return BindException.class;
588
}
589
590
@Override
591
protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) {
592
StringBuilder description = new StringBuilder();
593
StringBuilder action = new StringBuilder();
594
595
description.append("Binding to target ")
596
.append(cause.getTarget().getClass().getSimpleName())
597
.append(" failed:");
598
599
action.append("Update your application configuration:\n\n");
600
601
cause.getFieldErrors().forEach(error -> {
602
description.append("\n Property: ").append(error.getField())
603
.append("\n Value: ").append(error.getRejectedValue())
604
.append("\n Reason: ").append(error.getDefaultMessage());
605
606
action.append("# ").append(error.getDefaultMessage()).append("\n")
607
.append(error.getField()).append("=<correct-value>\n\n");
608
});
609
610
return new FailureAnalysis(description.toString(), action.toString(), cause);
611
}
612
}
613
```
614
615
### Context Initializers and Listeners
616
617
Interfaces for customizing application context initialization and listening to events.
618
619
```java { .api }
620
/**
621
* Callback interface for initializing a Spring ConfigurableApplicationContext
622
* prior to being refreshed
623
*/
624
@FunctionalInterface
625
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
626
627
/**
628
* Initialize the given application context
629
* @param applicationContext the application to configure
630
*/
631
void initialize(C applicationContext);
632
}
633
634
/**
635
* Interface to be implemented by application event listeners
636
*/
637
@FunctionalInterface
638
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
639
640
/**
641
* Handle an application event
642
* @param event the event to respond to
643
*/
644
void onApplicationEvent(E event);
645
}
646
```
647
648
**Usage Examples:**
649
650
```java
651
// Custom application context initializer
652
public class CustomApplicationContextInitializer
653
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
654
655
@Override
656
public void initialize(ConfigurableApplicationContext applicationContext) {
657
// Add custom property sources
658
ConfigurableEnvironment environment = applicationContext.getEnvironment();
659
MutablePropertySources propertySources = environment.getPropertySources();
660
661
Properties customProps = new Properties();
662
customProps.put("app.initialized-by", "CustomInitializer");
663
customProps.put("app.initialization-time", Instant.now().toString());
664
665
propertySources.addFirst(new PropertiesPropertySource("custom", customProps));
666
667
// Register custom beans programmatically
668
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext;
669
registry.registerBeanDefinition("customBean",
670
BeanDefinitionBuilder.genericBeanDefinition(CustomService.class).getBeanDefinition());
671
672
System.out.println("Custom application context initializer executed");
673
}
674
}
675
676
// Register the initializer
677
@SpringBootApplication
678
public class Application {
679
public static void main(String[] args) {
680
SpringApplication app = new SpringApplication(Application.class);
681
app.addInitializers(new CustomApplicationContextInitializer());
682
app.run(args);
683
}
684
}
685
```