Középhaladó Olvasási idő: ~8 perc

Lifecycle

SpringApplication, startup process, ApplicationRunner, CommandLineRunner

Életciklus

1. Definíció

A Spring Boot alkalmazás életciklusa a SpringApplication.run() hívástól az alkalmazás leállításáig tartó folyamat. Ez magában foglalja az ApplicationContext létrehozását, az auto-configuration-t, a bean-ek inicializálását, az embedded szerver indítását és a graceful shutdown-t.

A Spring Boot számos kiterjesztési pontot (hook) biztosít az életciklus különböző fázisaiban:

  • ApplicationRunner / CommandLineRunner — logika az indulás után
  • ApplicationEvent rendszer — eseményvezérelt integráció
  • SmartLifecycle — finomhangolt start/stop sorrend
  • @PreDestroy / DisposableBean — leállítási logika
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

2. Alapfogalmak

SpringApplication indulási folyamat

A SpringApplication.run() a következő lépéseket hajtja végre:

  1. SpringApplication példány létrehozása — alkalmazástípus felismerés (Servlet, Reactive, None)
  2. SpringApplicationRunListeners értesítése — starting esemény
  3. Environment előkészítése — property source-ok betöltése, profilok aktiválása
  4. Banner nyomtatás — a Spring Boot banner
  5. ApplicationContext létrehozása — a context típusának megfelelően
  6. Context refresh — bean definíciók betöltése, auto-configuration, bean inicializálás
  7. Runner-ek futtatása — ApplicationRunner, CommandLineRunner
  8. Készstartedrunning esemény

ApplicationRunner vs CommandLineRunner

@Component
public class MyAppRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // ApplicationArguments — strukturált argumentum-hozzáférés
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
    }
}

@Component
public class MyCmdRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // Nyers String tömb — egyszerű argumentumok
        System.out.println("Arguments: " + Arrays.toString(args));
    }
}

Alkalmazás események (Application Events)

Esemény Mikor?
ApplicationStartingEvent SpringApplication.run() eleje
ApplicationEnvironmentPreparedEvent Environment kész, context még nem
ApplicationContextInitializedEvent Context létrejött, bean-ek még nem
ApplicationPreparedEvent Bean definíciók betöltve, refresh előtt
ApplicationStartedEvent Context refresh kész, runner-ek előtt
ApplicationReadyEvent Runner-ek lefutottak, alkalmazás kész
ApplicationFailedEvent Hiba történt az indulás során

Leállítás (Shutdown)

A Spring Boot támogatja a graceful shutdown-t:

# Graceful shutdown
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

3. Gyakorlati használat

Indulás utáni inicializálás

@Component
@Order(1) // sorrend: alacsonyabb = előbb
public class DatabaseInitializer implements ApplicationRunner {
    private final DataSource dataSource;

    public DatabaseInitializer(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void run(ApplicationArguments args) {
        // Adatbázis séma inicializálás
        // A context teljesen inicializálva van
    }
}

@Component
@Order(2)
public class CacheWarmUp implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // Cache feltöltés az adatbázis inicializálás után
    }
}

Esemény hallgatók

@Component
public class StartupListener {

    @EventListener(ApplicationReadyEvent.class)
    public void onReady() {
        // Az alkalmazás teljesen kész
        System.out.println("Application is ready!");
    }

    @EventListener(ApplicationStartedEvent.class)
    public void onStarted() {
        // Context refresh kész, runner-ek még nem futottak
    }
}

Korai esemény hallgatók (spring.factories)

Az ApplicationStartingEvent és ApplicationEnvironmentPreparedEvent a context létrehozása előtt történik, így nem regisztrálhatók @Component-ként:

public class EarlyEventListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("Application starting...");
    }
}

Regisztráció:

# META-INF/spring.factories
org.springframework.context.ApplicationListener=\
  com.example.EarlyEventListener

SpringApplication testreszabása

public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.setAdditionalProfiles("metrics");
        app.setDefaultProperties(Map.of("server.port", "9090"));
        app.run(args);
    }
}

4. Kód példák

SmartLifecycle implementáció

@Component
public class MessageConsumer implements SmartLifecycle {
    private volatile boolean running = false;

    @Override
    public void start() {
        running = true;
        // Kafka/RabbitMQ consumer indítás
    }

    @Override
    public void stop() {
        running = false;
        // Consumer leállítás
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public int getPhase() {
        return Integer.MAX_VALUE; // utolsóként indul, elsőként áll le
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }
}

Bean lifecycle annotációk

@Component
public class ConnectionPool {
    private HikariDataSource dataSource;

    @PostConstruct
    public void init() {
        // Bean létrehozás után — inicializálás
        dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:postgresql://localhost/mydb");
    }

    @PreDestroy
    public void cleanup() {
        // Alkalmazás leállításkor — erőforrás felszabadítás
        if (dataSource != null) {
            dataSource.close();
        }
    }
}

SpringApplicationBuilder (fluent API)

public class MyApp {
    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApp.class)
            .bannerMode(Banner.Mode.LOG)
            .profiles("production")
            .properties("server.port=8443")
            .listeners(new EarlyEventListener())
            .run(args);
    }
}

Startup actuator (Boot 3.x)

management.endpoint.startup.enabled=true
management.endpoints.web.exposure.include=startup
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        app.setApplicationStartup(new BufferingApplicationStartup(10000));
        app.run(args);
    }
}

5. Trade-offok

Szempont ApplicationRunner CommandLineRunner
Argumentumok ApplicationArguments (strukturált) String[] (nyers)
Option parsing Beépített (--key=value) Kézi parsing
Használat Összetett argumentumok Egyszerű szkriptek
Szempont @PostConstruct ApplicationRunner
Futási idő Bean létrehozáskor Teljes context kész
Más bean-ek Nem garantált, hogy mind kész Mind inicializálva
Scope Egy bean Alkalmazás szintű
Szempont @EventListener ApplicationListener
Regisztráció @Component (context-ben) spring.factories (korai)
Korai események ❌ Nem elérhető ✅ Elérhető
Egyszerűség ✅ Annotáció-alapú ❌ Interface implementáció

6. Gyakori hibák

❌ @PostConstruct-ban más bean-ekre támaszkodás

@Component
public class ReportService {
    @Autowired
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        // KOCKÁZATOS: a UserRepository bean inicializálva van,
        // de a teljes context (pl. tranzakció manager) nem feltétlenül
        long count = userRepository.count();
    }
}

✅ ApplicationRunner használata

@Component
public class ReportService implements ApplicationRunner {
    private final UserRepository userRepository;

    public ReportService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public void run(ApplicationArguments args) {
        // BIZTONSÁGOS: a teljes context inicializálva van
        long count = userRepository.count();
    }
}

❌ Korai esemény @Component-ként

// HIBA: az ApplicationStartingEvent a context létrehozása ELŐTT történik
@Component // ← nem fogja megkapni az eseményt!
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) { }
}

✅ Regisztráció spring.factories-ban

# META-INF/spring.factories
org.springframework.context.ApplicationListener=\
  com.example.MyListener

❌ Runner sorrend keverése

Ha több Runner van és egymásra épülnek, de nincs @Order → véletlenszerű sorrend.

@Component
@Order(1) // ← MINDIG adj sorrendet, ha függ egymástól
public class FirstRunner implements ApplicationRunner { ... }

@Component
@Order(2)
public class SecondRunner implements ApplicationRunner { ... }

7. Mélyebb összefüggések

Teljes indulási sorrend

1. main() meghívja SpringApplication.run()
2. SpringApplication példány jön létre
3. SpringApplicationRunListeners.starting()
4. Environment előkészítés (property source-ok, profilok)
5. Banner nyomtatás
6. ApplicationContext létrehozás (típus: Servlet/Reactive/None)
7. Context refresh:
   a. BeanFactory előkészítés
   b. BeanDefinition-ök betöltése
   c. BeanFactoryPostProcessor-ok futtatása
   d. Bean-ek példányosítása + dependency injection
   e. BeanPostProcessor-ok (AOP proxy, @PostConstruct)
   f. SmartLifecycle.start()
8. ApplicationStartedEvent
9. ApplicationRunner / CommandLineRunner futtatás
10. ApplicationReadyEvent
11. Alkalmazás fut (kéréseket fogad)

Graceful shutdown részletek

server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

A graceful shutdown:

  1. Nem fogad új kéréseket
  2. Megvárja a folyamatban lévő kéréseket (timeout-ig)
  3. SmartLifecycle.stop() — fordított phase sorrendben
  4. @PreDestroy / DisposableBean.destroy()
  5. ApplicationContext bezárás

Startup teljesítmény mérés

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        // Boot 3.x: startup lépések mérése
        app.setApplicationStartup(new BufferingApplicationStartup(10000));
        app.run(args);
    }
}
// GET /actuator/startup → JSON részletes időmérés

Liveness és Readiness szondák

# Kubernetes szondák
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
  • Liveness: az alkalmazás fut-e (nem akadt-e meg)
  • Readiness: kész-e kéréseket fogadni (runner-ek lefutottak)

8. Interjúkérdések

  1. Mi a különbség az ApplicationRunner és a CommandLineRunner között? ApplicationRunner: ApplicationArguments (strukturált, option parsing). CommandLineRunner: String[] (nyers). Mindkettő a teljes context inicializálás után fut.

  2. Milyen sorrendben történik a Spring Boot indulás? Environment → Banner → Context create → Refresh (bean-ek) → Started event → Runner-ek → Ready event.

  3. Mi a @PostConstruct és az ApplicationRunner közötti különbség? @PostConstruct: az adott bean létrehozásakor fut (nem garantált, hogy minden más bean kész). ApplicationRunner: a teljes context inicializálás után fut.

  4. Hogyan hallgatod a korai eseményeket? spring.factories-ben regisztrálod az ApplicationListener-t, mert a context létrehozása előtti események nem érhetők el @Component-ként.

  5. Mi a graceful shutdown? Nem fogad új kéréseket, megvárja a folyamatban lévőeket, SmartLifecycle.stop(), @PreDestroy, context bezárás.

  6. Mi a SmartLifecycle? Interfész finomhangolt start/stop sorrendhez. A phase értéke határozza meg a sorrendet. Alacsony phase = előbb indul, később áll le.

  7. Hogyan méred az indulási teljesítményt? BufferingApplicationStartup + /actuator/startup endpoint. Boot 3.x feature.

9. Szószedet

Fogalom Jelentés
SpringApplication A Spring Boot alkalmazás belépési pontja és bootstrap osztálya
ApplicationRunner Indulás utáni logika futtatása (ApplicationArguments)
CommandLineRunner Indulás utáni logika futtatása (String[])
ApplicationEvent Az alkalmazás életciklus eseményei
ApplicationReadyEvent Az alkalmazás teljesen kész (runner-ek lefutottak)
SmartLifecycle Finomhangolt start/stop sorrend interfész
@PostConstruct Bean létrehozás utáni inicializálás
@PreDestroy Alkalmazás leállítás előtti cleanup
Graceful shutdown Kérések befejezése leállítás előtt
BufferingApplicationStartup Indulási teljesítmény mérő (Boot 3.x)

10. Gyorsreferencia

Indulási sorrend:
  main() → SpringApplication.run()
  → Environment → Banner → Context create
  → Refresh (beans) → SmartLifecycle.start()
  → ApplicationStartedEvent
  → ApplicationRunner / CommandLineRunner
  → ApplicationReadyEvent → alkalmazás fut

Leállítási sorrend:
  SIGTERM → nem fogad új kéréseket
  → várakozás folyamatban lévőkre
  → SmartLifecycle.stop() (fordított phase)
  → @PreDestroy / DisposableBean
  → Context bezárás
// ApplicationRunner (strukturált argumentumok)
@Component @Order(1)
public class MyRunner implements ApplicationRunner {
    public void run(ApplicationArguments args) { }
}

// CommandLineRunner (nyers String[])
@Component @Order(2)
public class MyCmdRunner implements CommandLineRunner {
    public void run(String... args) { }
}

// Esemény hallgató
@EventListener(ApplicationReadyEvent.class)
public void onReady() { }

// SmartLifecycle (start/stop sorrend)
implements SmartLifecycle { getPhase(); start(); stop(); }
# Graceful shutdown
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

# Kubernetes szondák
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true

# Startup teljesítmény mérés
management.endpoint.startup.enabled=true

🎮 Játékok

10 kérdés