Capire il Spring lifecycle è fondamentale per lavorare bene con Spring Framework e Spring Boot.
Quando un’applicazione parte, il container crea i bean, inietta le dipendenze, esegue i metodi di inizializzazione,
gestisce le richieste HTTP e, alla fine, chiude ordinatamente le risorse durante lo shutdown.
✅ In questa guida vedremo: il lifecycle of Spring bean, il
spring bean lifecycle, il spring boot lifecycle, il
spring framework lifecycle, il spring request lifecycle e la proprietà
spring.lifecycle.timeout-per-shutdown-phase.
🧭 Che cosa significa Spring Lifecycle
Con l’espressione Spring lifecycle si fa riferimento a diversi cicli di vita gestiti dal framework.
Il più conosciuto è il ciclo di vita dei bean, ma non è l’unico. In un’applicazione reale dobbiamo considerare anche
il ciclo di vita dell’intera applicazione Spring Boot e il ciclo di vita delle richieste HTTP.
Per questo motivo, quando si cerca una keyword come spring lifecycle, è importante distinguere almeno
tre livelli:
🌱 Bean lifecycle
Riguarda la vita di un singolo bean gestito dall’IoC container di Spring.
🚀 Application lifecycle
Riguarda lo startup, gli eventi, i runner e lo shutdown dell’applicazione Spring Boot.
🌐 Request lifecycle
Riguarda il percorso di una richiesta HTTP dentro Spring MVC o Spring Boot.
🌱 Lifecycle of Spring Bean
Quando si parla di lifecycle of Spring bean o di spring bean lifecycle, ci si riferisce
al processo con cui Spring crea, configura, inizializza, rende disponibile e infine distrugge un bean.
Un bean è un oggetto gestito dal container Spring. Non viene creato manualmente con new dal nostro codice,
ma viene istanziato e amministrato dal framework. Questo permette a Spring di occuparsi di dependency injection,
configurazione, inizializzazione, proxy, callback e distruzione.
💡 Regola pratica: un bean Spring nasce, riceve le dipendenze, viene inizializzato, viene usato
dall’applicazione e viene distrutto quando il contesto viene chiuso.
🔄 Spring Bean Lifecycle: ordine completo
In forma semplificata, il spring bean lifecycle segue questa sequenza:
- Istanziazione del bean
- Iniezione delle dipendenze
- Callback Aware, se presenti
- BeanPostProcessor before initialization
- Metodi di inizializzazione
- BeanPostProcessor after initialization
- Bean pronto all’uso
- Distruzione del bean alla chiusura del contesto
Questo flusso è molto importante perché evita confusione tra costruttore, dependency injection,
@PostConstruct, callback del container e distruzione del bean.
Schema rapido del ciclo di vita
Istanziazione
↓
Dependency Injection
↓
Aware interfaces
↓
BeanPostProcessor before init
↓
@PostConstruct
↓
InitializingBean.afterPropertiesSet()
↓
Custom initMethod
↓
BeanPostProcessor after init
↓
Bean pronto all'uso
↓
@PreDestroy
↓
DisposableBean.destroy()
↓
Custom destroyMethod
🛠️ Modi per gestire inizializzazione e distruzione
Spring mette a disposizione diversi modi per gestire il ciclo di vita di un bean. Alcuni sono moderni e molto usati,
altri sono più legati ad applicazioni legacy o configurazioni XML.
✅ Approccio consigliato: @PostConstruct e @PreDestroy
L’approccio più leggibile consiste nell’usare le annotazioni @PostConstruct e @PreDestroy.
Nelle versioni moderne di Spring Boot si usa il package jakarta.annotation.
package com.example.lifecycle;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
public class NotificationService {
public NotificationService() {
System.out.println("1. Costruttore: bean istanziato");
}
@PostConstruct
public void init() {
System.out.println("2. @PostConstruct: bean inizializzato");
}
public void sendMessage(String message) {
System.out.println("3. Invio messaggio: " + message);
}
@PreDestroy
public void cleanup() {
System.out.println("4. @PreDestroy: cleanup del bean");
}
}
✔️ Vantaggio: il codice è chiaro. Guardando la classe si capisce subito quale metodo inizializza
il bean e quale metodo viene eseguito prima della distruzione.
✅ Esempio di avvio e chiusura del contesto
package com.example.lifecycle;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class LifecycleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(LifecycleApplication.class, args);
NotificationService service = context.getBean(NotificationService.class);
service.sendMessage("Ciao Spring Lifecycle");
context.close();
}
}
Output atteso:
1. Costruttore: bean istanziato
2. @PostConstruct: bean inizializzato
3. Invio messaggio: Ciao Spring Lifecycle
4. @PreDestroy: cleanup del bean
✅ InitializingBean e DisposableBean
Un altro modo per partecipare al spring framework lifecycle è implementare le interfacce
InitializingBean e DisposableBean.
package com.example.lifecycle;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class FileStorageService implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
System.out.println("afterPropertiesSet(): inizializzazione");
}
@Override
public void destroy() {
System.out.println("destroy(): cleanup");
}
}
⚠️ Attenzione: questo approccio funziona bene, ma accoppia direttamente la classe alle interfacce
di Spring. Per codice applicativo moderno, spesso è preferibile usare @PostConstruct e
@PreDestroy.
✅ Metodo custom con @Bean(initMethod, destroyMethod)
Questo approccio è molto utile quando devi gestire classi esterne o librerie di terze parti che non puoi modificare
direttamente.
package com.example.lifecycle;
public class ExternalClient {
public void connect() {
System.out.println("Connessione aperta");
}
public void disconnect() {
System.out.println("Connessione chiusa");
}
}
package com.example.lifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(initMethod = "connect", destroyMethod = "disconnect")
public ExternalClient externalClient() {
return new ExternalClient();
}
}
🏛️ Configurazione XML
L’approccio XML è meno frequente nelle applicazioni moderne, ma può ancora comparire in progetti legacy.
<bean id="beanLifecycle"
class="com.example.lifecycle.BeanLifecycle"
init-method="init"
destroy-method="cleanup"/>
🧠 Ordine delle callback di inizializzazione e distruzione
Se più meccanismi sono configurati sullo stesso bean, Spring segue un ordine preciso.
| Fase | Ordine di esecuzione |
|---|---|
| Inizializzazione | @PostConstruct → afterPropertiesSet() → initMethod |
| Distruzione | @PreDestroy → destroy() → destroyMethod |
🔥 Domanda classica da colloquio: se ho sia @PostConstruct, sia
afterPropertiesSet(), sia un initMethod, quale parte prima?
Risposta: @PostConstruct → afterPropertiesSet() → initMethod.
🧷 BeanPostProcessor: livello avanzato del Spring Lifecycle
Il BeanPostProcessor permette di intervenire prima e dopo l’inizializzazione dei bean. È uno strumento
avanzato, usato spesso da Spring stesso per creare proxy, applicare logiche trasversali o personalizzare il comportamento
dei bean.
package com.example.lifecycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("Before initialization: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("After initialization: " + beanName);
return bean;
}
}
👀 Interfacce Aware nel lifecycle di Spring
Alcuni bean possono implementare interfacce speciali per ricevere informazioni dal container. Queste interfacce fanno
parte del spring framework lifecycle.
BeanNameAwareBeanFactoryAwareApplicationContextAware
package com.example.lifecycle;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class AwareBean implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
System.out.println("ApplicationContext ricevuto");
}
}
🚀 Spring Boot Lifecycle
Il spring boot lifecycle include il ciclo di vita dei bean, ma aggiunge anche tutto ciò che riguarda
l’avvio e lo spegnimento dell’applicazione.
Quando esegui:
SpringApplication.run(MyApplication.class, args);
Spring Boot prepara l’ambiente, crea l’ApplicationContext, esegue l’autoconfigurazione, inizializza i bean, avvia
eventualmente il server web embedded e pubblica eventi applicativi.
Fasi principali del Spring Boot Lifecycle
- Bootstrap di
SpringApplication - Preparazione dell’ambiente e dei profili
- Creazione dell’
ApplicationContext - Registrazione e inizializzazione dei bean
- Avvio del server web embedded, se presente
- Esecuzione di runner ed eventi
- Applicazione pronta
- Shutdown e distruzione dei bean
Eventi utili nel Spring Boot Lifecycle
Spring Boot pubblica diversi eventi durante l’avvio. Uno dei più utili è ApplicationReadyEvent, che viene
emesso quando l’applicazione è pronta.
package com.example.lifecycle;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupListener {
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
System.out.println("Applicazione pronta a ricevere richieste");
}
}
CommandLineRunner e ApplicationRunner
Nel spring boot lifecycle, CommandLineRunner e ApplicationRunner permettono
di eseguire codice dopo l’avvio del contesto.
package com.example.lifecycle;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class SampleRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("CommandLineRunner eseguito");
}
}
package com.example.lifecycle;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class SampleApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
System.out.println("ApplicationRunner eseguito");
}
}
🌐 Spring Request Lifecycle
Il spring request lifecycle descrive il percorso di una richiesta HTTP dentro un’applicazione Spring MVC
o Spring Boot.
Non riguarda la vita di un bean singleton, ma il flusso della richiesta dal client al controller e poi di nuovo al client.
Client HTTP
↓
Filter
↓
DispatcherServlet
↓
HandlerMapping
↓
Interceptor preHandle
↓
Controller
↓
Service
↓
Repository
↓
Response
↓
Interceptor postHandle / afterCompletion
↓
Client
Esempio di controller
package com.example.lifecycle;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello Spring Request Lifecycle";
}
}
Esempio di interceptor
package com.example.lifecycle;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
System.out.println("preHandle -> " + request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("afterCompletion -> " + request.getRequestURI());
}
}
Registrazione dell’interceptor
package com.example.lifecycle;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoggingInterceptor loggingInterceptor;
public WebConfig(LoggingInterceptor loggingInterceptor) {
this.loggingInterceptor = loggingInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
📦 Request Scope e lifecycle dei bean di richiesta
Nel contesto del spring request lifecycle, esistono bean che vivono solo durante una singola richiesta HTTP.
Questo comportamento si ottiene con @RequestScope.
package com.example.lifecycle;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class RequestContextData {
public RequestContextData() {
System.out.println("Nuovo bean RequestScope creato");
}
}
🧠 Cosa significa? Ogni richiesta HTTP riceve una nuova istanza del bean.
Il lifecycle di un bean request-scoped è quindi diverso dal lifecycle di un bean singleton.
⏱️ Spring Lifecycle Timeout Per Shutdown Phase
La keyword spring lifecycle timeout per shutdown phase si riferisce a una proprietà molto importante
nello shutdown ordinato di Spring Boot:
spring.lifecycle.timeout-per-shutdown-phase
Questa proprietà definisce quanto tempo Spring Boot deve attendere per ogni fase di shutdown prima di proseguire
con la chiusura dell’applicazione.
Configurazione in application.properties
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
Configurazione in application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
💡 In pratica: se configuri spring.lifecycle.timeout-per-shutdown-phase=30s,
Spring Boot può attendere fino a 30 secondi per ogni fase di spegnimento prima di passare alla fase successiva.
🛑 Graceful Shutdown in Spring Boot
Il graceful shutdown permette all’applicazione di spegnersi in modo ordinato. Invece di interrompere immediatamente
tutto, Spring Boot prova a completare le richieste in corso e a chiudere correttamente le risorse.
Una configurazione tipica è questa:
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=20s
Cosa succede durante lo shutdown?
- Spring Boot smette di accettare nuove richieste.
- Le richieste già in corso possono terminare.
- I componenti lifecycle vengono fermati.
- I metodi
@PreDestroyvengono eseguiti. - Il contesto Spring viene chiuso.
Esempio di cleanup con @PreDestroy
package com.example.lifecycle;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;
@Service
public class ReportService {
@PreDestroy
public void shutdown() {
System.out.println("Chiusura del ReportService");
}
}
🧩 SmartLifecycle e shutdown per fasi
Per capire bene spring.lifecycle.timeout-per-shutdown-phase, è utile conoscere l’interfaccia
SmartLifecycle. Questa interfaccia permette a un bean di partecipare in modo più evoluto all’avvio e
allo spegnimento dell’applicazione.
package com.example.lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class MessageListenerLifecycle implements SmartLifecycle {
private boolean running = false;
@Override
public void start() {
System.out.println("Avvio listener");
running = true;
}
@Override
public void stop() {
System.out.println("Stop listener");
running = false;
}
@Override
public void stop(Runnable callback) {
System.out.println("Stop async listener");
running = false;
callback.run();
}
@Override
public boolean isRunning() {
return running;
}
@Override
public int getPhase() {
return 0;
}
@Override
public boolean isAutoStartup() {
return true;
}
}
La fase, o phase, serve a stabilire l’ordine di avvio e spegnimento dei componenti. Durante lo shutdown,
Spring gestisce i componenti per fasi e applica il timeout configurato con
spring.lifecycle.timeout-per-shutdown-phase.
🧭 Spring Framework Lifecycle vs Spring Boot Lifecycle
| Concetto | Cosa comprende |
|---|---|
| Spring Framework Lifecycle | IoC container, bean lifecycle, callback, BeanPostProcessor, Aware interfaces. |
| Spring Boot Lifecycle | Startup, autoconfigurazione, runner, eventi, web server, graceful shutdown. |
| Spring Request Lifecycle | Percorso di una richiesta HTTP tra filtri, DispatcherServlet, controller e risposta. |
🧨 Errori comuni sul Spring Lifecycle
❌ Confondere costruttore e inizializzazione
Il costruttore viene eseguito prima che tutte le dipendenze siano completamente pronte. Per inizializzare il bean
dopo l’iniezione delle dipendenze, usa @PostConstruct.
⚠️ Dimenticare di chiudere il contesto
Se il contesto non viene chiuso correttamente, i metodi di destroy potrebbero non essere eseguiti.
⚠️ Ignorare il graceful shutdown
In produzione, specialmente con microservizi e richieste HTTP, è importante configurare
server.shutdown=graceful e un timeout adeguato.
✅ Best practice
- Usa
@PostConstructper inizializzare risorse dopo la dependency injection. - Usa
@PreDestroyper liberare risorse prima dello shutdown. - Evita logica pesante nel costruttore.
- Usa
@Bean(initMethod, destroyMethod)per classi di terze parti. - Configura il graceful shutdown in produzione.
- Usa
ApplicationReadyEventper operazioni da eseguire quando l’applicazione è pronta. - Usa
SmartLifecyclesolo quando hai bisogno di controllare startup e shutdown per fasi.
❓ FAQ sul Spring Lifecycle
Che cos’è il lifecycle of Spring bean?
È il processo con cui Spring crea, configura, inizializza, usa e distrugge un bean gestito dal container.
Qual è il ciclo di vita di un bean in Spring?
Il ciclo di vita tipico è: istanziazione, dependency injection, callback aware, BeanPostProcessor, inizializzazione,
utilizzo e distruzione.
Che differenza c’è tra Spring bean lifecycle e Spring Boot lifecycle?
Il spring bean lifecycle riguarda il singolo bean. Il spring boot lifecycle riguarda
l’intera applicazione, inclusi startup, eventi, runner, server web e shutdown.
A cosa serve spring.lifecycle.timeout-per-shutdown-phase?
Serve a configurare il tempo massimo che Spring Boot attende per ogni fase di shutdown durante lo spegnimento ordinato
dell’applicazione.
Come si abilita il graceful shutdown in Spring Boot?
Si abilita configurando:
server.shutdown=graceful
Che cos’è il spring request lifecycle?
È il percorso che una richiesta HTTP compie dentro Spring MVC o Spring Boot, passando da filtri, DispatcherServlet,
mapping, interceptor, controller, service e repository.
Quando usare ApplicationReadyEvent?
Quando vuoi eseguire codice solo dopo che l’applicazione è completamente avviata e pronta a ricevere richieste.
Quando usare CommandLineRunner?
Quando vuoi eseguire codice subito dopo l’avvio del contesto Spring, ad esempio per log iniziali, caricamento dati o
operazioni di bootstrap.
🎯 Conclusione
Il Spring lifecycle è uno degli argomenti più importanti per comprendere davvero come funziona Spring.
Non riguarda soltanto la creazione dei bean, ma anche il modo in cui l’applicazione parte, gestisce richieste, pubblica
eventi e si spegne in modo ordinato.
Il lifecycle of Spring bean spiega come nasce e muore un bean. Il spring boot lifecycle
estende questa visione all’intera applicazione. Il spring request lifecycle descrive invece il percorso
di una richiesta HTTP.
La proprietà spring.lifecycle.timeout-per-shutdown-phase è particolarmente importante nelle applicazioni
moderne, perché permette di controllare il tempo massimo di attesa per ogni fase di spegnimento durante il graceful shutdown.
📌 Riassunto finale: conoscere il lifecycle in Spring ti aiuta a scrivere applicazioni più pulite,
più prevedibili, più robuste e più adatte alla produzione.