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:
- SpringApplication példány létrehozása — alkalmazástípus felismerés (Servlet, Reactive, None)
- SpringApplicationRunListeners értesítése —
startingesemény - Environment előkészítése — property source-ok betöltése, profilok aktiválása
- Banner nyomtatás — a Spring Boot banner
- ApplicationContext létrehozása — a context típusának megfelelően
- Context refresh — bean definíciók betöltése, auto-configuration, bean inicializálás
- Runner-ek futtatása — ApplicationRunner, CommandLineRunner
- Kész —
started→runningesemé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:
- Nem fogad új kéréseket
- Megvárja a folyamatban lévő kéréseket (timeout-ig)
- SmartLifecycle.stop() — fordított phase sorrendben
- @PreDestroy / DisposableBean.destroy()
- 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
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.
Milyen sorrendben történik a Spring Boot indulás? Environment → Banner → Context create → Refresh (bean-ek) → Started event → Runner-ek → Ready event.
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.
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.
Mi a graceful shutdown? Nem fogad új kéréseket, megvárja a folyamatban lévőeket, SmartLifecycle.stop(), @PreDestroy, context bezárás.
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.
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