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:

  1. Istanziazione del bean
  2. Iniezione delle dipendenze
  3. Callback Aware, se presenti
  4. BeanPostProcessor before initialization
  5. Metodi di inizializzazione
  6. BeanPostProcessor after initialization
  7. Bean pronto all’uso
  8. 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.

  • BeanNameAware
  • BeanFactoryAware
  • ApplicationContextAware
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

  1. Bootstrap di SpringApplication
  2. Preparazione dell’ambiente e dei profili
  3. Creazione dell’ApplicationContext
  4. Registrazione e inizializzazione dei bean
  5. Avvio del server web embedded, se presente
  6. Esecuzione di runner ed eventi
  7. Applicazione pronta
  8. 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 @PreDestroy vengono 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 @PostConstruct per inizializzare risorse dopo la dependency injection.
  • Usa @PreDestroy per 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 ApplicationReadyEvent per operazioni da eseguire quando l’applicazione è pronta.
  • Usa SmartLifecycle solo 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.

Categorie: Spring