Zum Hauptinhalt springen

Woche 2, Tag 5 — Bean Lifecycle

Ziel

Heute verstehst du den Spring Bean Lifecycle.

Die Kernfragen:

  1. Was ist der Bean Lifecycle?
  2. Was passiert, wenn Spring einen Bean erzeugt?
  3. Wann werden Dependencies injiziert?
  4. Was ist @PostConstruct?
  5. Was ist @PreDestroy?
  6. Was sind InitializingBean und DisposableBean?
  7. Was sind initMethod und destroyMethod in @Bean?
  8. Was ist BeanPostProcessor?
  9. Wie hängt BeanPostProcessor mit AOP und Proxies zusammen?
  10. Welche typischen Prüfungsfallen gibt es?

1. Kurz-Wiederholung von Tag 4

An Tag 4 hast du gelernt:

  • Bean Scope steuert, wie lange ein Bean lebt.
  • Der Default Scope ist singleton.
  • Spring singleton bedeutet eine Bean-Instanz pro Spring Container.
  • Singleton Beans sollten meist stateless sein.
  • Prototype Scope erzeugt bei jeder Anfrage eine neue Instanz.
  • Request Scope erzeugt einen Bean pro HTTP Request.
  • Session Scope erzeugt einen Bean pro HTTP Session.
  • Ein Scoped Proxy erlaubt es einem Singleton, von einem Request- oder Session-scoped Bean abzuhängen.

Merksatz:


Singleton bedeutet ein Bean pro Spring Container.
Prototype bedeutet bei jeder Anfrage ein neuer Bean.

Heute lernst du, was von der Bean-Erzeugung bis zur Bean-Zerstörung passiert.


2. Was ist Bean Lifecycle?

Bean Lifecycle bedeutet:

Die Schritte, die ein Spring Bean von der Erzeugung bis zur Zerstörung durchläuft.

Spring erzeugt nicht nur Objekte.

Spring kann auch:


1. Den Bean instanziieren
2. Dependencies injizieren
3. Bean-Namen setzen
4. Application Context setzen
5. Post-Processors ausführen
6. Initialization Callbacks ausführen
7. Proxies erzeugen
8. Den Bean nutzen
9. Destruction Callbacks ausführen
10. Den Bean zerstören, wenn der Context schließt

Kurze Definition:

Bean Lifecycle ist der Prozess, den Spring befolgt, um Beans zu erzeugen, zu konfigurieren, zu initialisieren, zu nutzen und zu zerstören.


3. Vereinfachter Bean Lifecycle

Für die Prüfung merk dir diese vereinfachte Reihenfolge:


1. BeanDefinition wird gelesen
2. Bean wird instanziiert
3. Dependencies werden injiziert
4. Aware Callbacks laufen
5. BeanPostProcessor vor der Initialisierung
6. Initialization Callback läuft
7. BeanPostProcessor nach der Initialisierung
8. Bean ist einsatzbereit
9. Destruction Callback läuft, wenn der Context schließt

Merksatz:

Erzeugen, injizieren, initialisieren, nutzen, zerstören.


4. Lifecycle als Bild


BeanDefinition

Objekt instanziieren

Dependencies injizieren

Aware Callbacks

BeanPostProcessor vor init

@PostConstruct / afterPropertiesSet / init method

BeanPostProcessor nach init

Bean bereit

Application nutzt Bean

@PreDestroy / destroy / destroy method

Bean zerstört

5. Beispiel-Bean


@Service
public class EmailService {

private final EmailClient emailClient;

public EmailService(EmailClient emailClient) {
this.emailClient = emailClient;
}

@PostConstruct
public void init() {
System.out.println("EmailService initialized");
}

@PreDestroy
public void cleanup() {
System.out.println("EmailService destroyed");
}
}

Spring macht Folgendes:


1. Erzeugt EmailClient Bean
2. Erzeugt EmailService Bean
3. Injiziert EmailClient über den Constructor
4. Ruft init() auf
5. Nutzt EmailService
6. Ruft cleanup() auf, wenn der Context schließt

6. Schritt 1 — BeanDefinition wird registriert

Bevor Spring das Objekt erzeugt, registriert es eine BeanDefinition.

Beispiel:


@Service
public class TaskService {
}

Spring scannt die Klasse und erzeugt Metadaten:


BeanDefinition:
- bean name: taskService
- class: TaskService
- scope: singleton
- dependencies: maybe TaskRepository
- lazy: false

Merke:


BeanDefinition = Rezept
Bean = tatsächliches Objekt

7. Schritt 2 — Bean wird instanziiert

Instantiation bedeutet:

Spring erzeugt das Java-Objekt.

Beispiel:


@Service
public class TaskService {
}

Spring erzeugt intern etwas wie:


new TaskService(...)

Das passiert aber innerhalb des Containers.

Wichtig:

Instantiation erzeugt das Objekt, aber Dependencies sind je nach Injection-Stil noch nicht vollständig konfiguriert.

Bei Constructor Injection werden Dependencies während der Konstruktion gebraucht.


8. Schritt 3 — Dependencies werden injiziert

Spring injiziert Dependencies.

Beispiel:


@Service
public class TaskService {

private final TaskRepository taskRepository;

public TaskService(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
}

Spring findet einen TaskRepository Bean und injiziert ihn.

Bei Constructor Injection:


Dependency wird während der Objekterzeugung bereitgestellt.

Bei Setter- oder Field Injection:


Objekt wird zuerst erzeugt, dann werden Dependencies gesetzt.

9. Constructor Injection und Lifecycle

Constructor Injection passiert während der Instantiation.

Beispiel:


public TaskService(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}

Spring kann TaskService nicht erzeugen, wenn es keinen TaskRepository bereitstellen kann.

Das ist gut, weil required Dependencies früh geprüft werden.

Merksatz:

Constructor Injection macht required Dependencies zum Teil der Bean-Erzeugung.


10. Field Injection und Lifecycle

Beispiel:


@Service
public class TaskService {

@Autowired
private TaskRepository taskRepository;
}

Lifecycle:


1. Spring erzeugt TaskService-Objekt.
2. Spring injiziert danach das taskRepository-Feld.

Das ist ein Grund, warum Field Injection weniger ideal ist.

Das Objekt existiert kurz ohne required Dependencies.


11. Schritt 4 — Aware Callbacks

Spring hat spezielle Aware-Interfaces.

Sie erlauben einem Bean, Spring-Infrastruktur-Objekte zu erhalten.

Beispiele:


BeanNameAware
BeanFactoryAware
ApplicationContextAware
EnvironmentAware
ResourceLoaderAware

Beispiel:


@Component
public class MyBean implements BeanNameAware {

@Override
public void setBeanName(String name) {
System.out.println("Bean name is " + name);
}
}

Das gibt dem Bean seinen Spring Bean-Namen.


12. ApplicationContextAware

Beispiel:


@Component
public class MyBean implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}

Das gibt dem Bean Zugriff auf den ApplicationContext.

Aber Vorsicht.

Normalerweise brauchst du das in normalem Business-Code nicht.

Bevorzuge Constructor Injection.


13. Soll ich Aware Interfaces oft nutzen?

Meist nein.

Aware Interfaces sind nützlich für Framework-Level- oder Infrastruktur-Code.

In normalem Service-Code bevorzuge:


@Service
public class OrderService {

private final PaymentService paymentService;

public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}

Vermeide unnötig:


applicationContext.getBean(PaymentService.class)

Merksatz:

Aware Interfaces sind mächtig, aber normaler Business-Code sollte Dependency Injection bevorzugen.


14. Schritt 5 — BeanPostProcessor vor der Initialisierung

Ein BeanPostProcessor kann Beans vor und nach der Initialisierung verändern.

Vor der Initialisierung:


postProcessBeforeInitialization()

Das passiert vor:


@PostConstruct
InitializingBean.afterPropertiesSet()
custom init method

Konzeptionell:


Bean erzeugt
Dependencies injiziert
BeanPostProcessor vor init
Initialization Callback
BeanPostProcessor nach init
Bean bereit

15. Schritt 6 — Initialization Callbacks

Initialization bedeutet:

Custom Logic ausführen, nachdem Dependencies injiziert wurden und bevor der Bean genutzt wird.

Es gibt mehrere Wege:


1. @PostConstruct
2. InitializingBean.afterPropertiesSet()
3. @Bean(initMethod = "...")

Am häufigsten im Application Code:


@PostConstruct

16. @PostConstruct

@PostConstruct markiert eine Methode, die nach der Dependency Injection läuft.

Beispiel:


@Component
public class CacheLoader {

private final ProductRepository productRepository;

public CacheLoader(ProductRepository productRepository) {
this.productRepository = productRepository;
}

@PostConstruct
public void loadCache() {
System.out.println("Loading cache after dependencies are ready");
}
}

Spring ruft loadCache() auf, nachdem Dependencies injiziert wurden.

Merksatz:

@PostConstruct läuft nach der Dependency Injection.


17. Wann @PostConstruct nutzen

Nutze @PostConstruct für Initialisierungslogik wie:


required Configuration validieren
Cache aufwärmen
Startup-Informationen loggen
externe Verbindung prüfen
In-Memory-Strukturen initialisieren

Beispiel:


@PostConstruct
public void validateConfig() {
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalStateException("API key is missing");
}
}

18. Vorsicht bei schwerer Arbeit in @PostConstruct

Pack nicht zu viel schwere Business-Logik in @PostConstruct.

Schlecht:


@PostConstruct
public void importAllCustomersFromExternalSystem() {
// huge long-running job
}

Warum schlecht?


Application Startup wird langsam.
Fehler können die ganze App stoppen.
Es mischt Startup Lifecycle mit Business Jobs.

Besser:


Nutze ApplicationRunner, CommandLineRunner, scheduled Jobs oder explizite Admin-Aktionen.

19. @PostConstruct und Constructor

Frage:

Warum nicht Initialisierungslogik direkt in den Constructor packen?

Weil nicht alle Spring Features im Constructor schon bereit sein können.

Im Constructor:


Das Objekt wird gerade erzeugt.
Einige Spring Lifecycle-Schritte sind noch nicht passiert.
AOP Proxy ist noch nicht bereit.
@PostConstruct ist noch nicht gelaufen.

Für dependency-basierte Initialisierung ist @PostConstruct oft sicherer.

Aber halte es einfach.


20. @PostConstruct-Beispiel mit Config


@Component
public class ExternalApiClient {

private final ExternalApiProperties properties;

public ExternalApiClient(ExternalApiProperties properties) {
this.properties = properties;
}

@PostConstruct
public void init() {
System.out.println("External API URL: " + properties.baseUrl());
}
}

Hier ist properties schon injiziert, wenn init() läuft.


21. InitializingBean

Spring hat auch ein Interface:


InitializingBean

Beispiel:


@Component
public class CacheLoader implements InitializingBean {

@Override
public void afterPropertiesSet() {
System.out.println("Initialization after properties are set");
}
}

afterPropertiesSet() läuft nach der Dependency Injection.


22. @PostConstruct vs. InitializingBean

Thema@PostConstructInitializingBean
StilAnnotationSpring Interface
Kopplung an Springgeringerhöher
Methodennamebeliebiger Methodennamemuss afterPropertiesSet sein
Häufig im App Codejaseltener
Am besten fürnormale App-InitialisierungSpring-spezifische Infrastruktur

Merksatz:

@PostConstruct ist meist bevorzugt, weil kein Spring Interface implementiert werden muss.


23. @Bean(initMethod = "...")

Für Beans, die mit @Bean erzeugt werden, kannst du eine Init-Methode definieren.

Beispiel:


@Configuration
public class ClientConfig {

@Bean(initMethod = "connect")
public ExternalClient externalClient() {
return new ExternalClient();
}
}

Klasse:


public class ExternalClient {

public void connect() {
System.out.println("Connecting...");
}
}

Spring ruft auf:


connect()

nach der Bean-Erzeugung.


24. Wann initMethod nutzen

Nutze initMethod, wenn:


die Klasse aus einer Third-Party Library kommt
du kein @PostConstruct hinzufügen kannst
die Klasse schon eine Init-Methode hat
du das Objekt mit @Bean erzeugst

Beispiel:


@Bean(initMethod = "start")
public ExternalServerClient externalServerClient() {
return new ExternalServerClient();
}

25. Initialisierungsreihenfolge

Wenn mehrere Initialisierungsmechanismen genutzt werden, ist die grobe Reihenfolge:


1. @PostConstruct
2. InitializingBean.afterPropertiesSet()
3. custom initMethod

Für den meisten Application Code: nicht alle drei zusammen nutzen.

Nutze einen klaren Ansatz.

Merksatz für die Prüfung:

Initialization Callbacks laufen nach der Dependency Injection und bevor der Bean einsatzbereit ist.


26. Schritt 7 — BeanPostProcessor nach der Initialisierung

Nach den Initialization Callbacks ruft Spring auf:


postProcessAfterInitialization()

Das ist sehr wichtig, weil viele Spring Features Bean Post-Processors nutzen.

Beispiele:


AOP Proxy-Erzeugung
@Transactional-Verarbeitung
@Autowired-Verarbeitung
@PostConstruct-Verarbeitung
@ConfigurationProperties Binding Support

AOP Proxies werden oft nach der Initialisierung erzeugt.

Merksatz:

BeanPostProcessors sind Extension Points, die Beans während der Erzeugung verändern oder wrappen können.


27. Was ist BeanPostProcessor?

BeanPostProcessor ist ein Interface, das Spring oder Custom Code erlaubt, Beans während des Lifecycles zu verändern.

Beispiel:


@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Before init: " + beanName);
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("After init: " + beanName);
return bean;
}
}

Das läuft für viele Beans im Context.


28. Was kann BeanPostProcessor?

Ein BeanPostProcessor kann:


Beans inspizieren
Bean Properties verändern
einen Bean durch ein anderes Objekt ersetzen
einen Bean mit einem Proxy wrappen
Annotationen auslösen
Framework-Verhalten anwenden

Das ist mächtig.

Es ist auch gefährlich, wenn man es unbedacht nutzt.


29. BeanPostProcessor und Proxies

Ein sehr wichtiges Konzept:

Ein BeanPostProcessor kann einen Proxy statt des ursprünglichen Beans zurückgeben.

Beispiel:


Original Bean: PaymentService
Zurückgegebener Bean: Transaction Proxy, der PaymentService wrappt

Wenn also ein anderer Bean PaymentService injiziert, bekommt er vielleicht den Proxy.

So funktionieren viele Spring Features.


30. BeanPostProcessor und @Transactional

Wenn Spring sieht:


@Service
public class PaymentService {

@Transactional
public void pay() {
}
}

kann Spring einen Proxy um PaymentService erzeugen.

Konzeptionell:


PaymentService Bean

BeanPostProcessor erkennt @Transactional

Erzeugt Proxy

Application nutzt Proxy

Der Proxy öffnet und schließt Transactions um Methodenaufrufe.

Das schauen wir uns in AOP und Transactions genauer an.


31. BeanPostProcessor und AOP

AOP hängt stark von Post-Processing ab.

Konzeptionell:


1. Spring erzeugt Bean.
2. Spring initialisiert Bean.
3. Ein BeanPostProcessor prüft, ob der Bean AOP braucht.
4. Wenn ja, erzeugt Spring einen Proxy.
5. Andere Beans bekommen den Proxy.

Deshalb ist Lifecycle für die Zertifizierung wichtig.

Merksatz:

AOP Proxies werden oft von BeanPostProcessors erzeugt.


32. Schritt 8 — Bean ist bereit

Nach Initialisierung und Post-Processing ist der Bean bereit.

Andere Beans können ihn nutzen.

Beispiel:


@RestController
public class PaymentController {

private final PaymentService paymentService;

public PaymentController(PaymentService paymentService) {
this.paymentService = paymentService;
}
}

Der injizierte paymentService kann sein:


echter PaymentService

oder:


PaymentService Proxy

— je nach Spring Features wie AOP, Transactions oder Security.


33. Schritt 9 — Destruction Callbacks

Wenn der Application Context schließt, zerstört Spring Singleton Beans.

Destruction Callbacks umfassen:


1. @PreDestroy
2. DisposableBean.destroy()
3. @Bean(destroyMethod = "...")

Am häufigsten:


@PreDestroy

34. @PreDestroy

@PreDestroy markiert eine Methode, die läuft, bevor der Bean zerstört wird.

Beispiel:


@Component
public class ExternalApiClient {

@PreDestroy
public void shutdown() {
System.out.println("Closing external API client");
}
}

Spring ruft das auf, wenn der Application Context herunterfährt.

Merksatz:

@PreDestroy läuft, bevor Spring den Bean zerstört.


35. Wann @PreDestroy nutzen

Nutze es für Cleanup:


Ressourcen schließen
Background Worker stoppen
Buffer flushen
Verbindungen schließen
Locks freigeben
Custom Clients herunterfahren

Beispiel:


@PreDestroy
public void cleanup() {
connection.close();
}

36. DisposableBean

Spring hat ein Interface:


DisposableBean

Beispiel:


@Component
public class ExternalClient implements DisposableBean {

@Override
public void destroy() {
System.out.println("Destroying bean");
}
}

Das funktioniert, aber es koppelt die Klasse an Spring.

Bevorzuge meist:


@PreDestroy

37. @Bean(destroyMethod = "...")

Für Third-Party-Klassen:


@Configuration
public class ClientConfig {

@Bean(destroyMethod = "close")
public ExternalClient externalClient() {
return new ExternalClient();
}
}

Spring ruft auf:


close()

wenn der Context herunterfährt.

Nützlich, wenn die Klasse eine Methode wie hat:


close()
shutdown()
stop()
destroy()

38. @PostConstruct- und @PreDestroy-Beispiel


@Component
public class FileStorageClient {

public FileStorageClient() {
System.out.println("Constructor");
}

@PostConstruct
public void init() {
System.out.println("Init after dependencies");
}

@PreDestroy
public void destroy() {
System.out.println("Cleanup before shutdown");
}
}

Erwartete Reihenfolge:


Constructor
Init after dependencies
Application läuft
Cleanup before shutdown

39. Prototype Beans und Destruction

Wichtige Prüfungsfalle:

Spring verwaltet den vollen Destruction Lifecycle von Prototype Beans nicht.

Beispiel:


@Component
@Scope("prototype")
public class ImportContext {

@PreDestroy
public void cleanup() {
System.out.println("cleanup");
}
}

Spring erzeugt und initialisiert den Prototype Bean.

Aber wenn der Application Context schließt, ruft Spring @PreDestroy nicht automatisch für jeden erzeugten Prototype auf.

Warum?

Weil Spring das Prototype-Objekt dem Aufrufer übergibt und es danach nicht mehr auf die gleiche Weise trackt.

Merksatz:

Prototype Beans werden von Spring erzeugt, aber Cleanup ist die Verantwortung des Aufrufers.


40. Singleton Beans und Destruction

Für Singleton Beans verwaltet Spring meist die Destruction.

Beispiel:


@Component
public class S3Client {

@PreDestroy
public void close() {
System.out.println("Closing S3 client");
}
}

Wenn der Context schließt, ruft Spring close() auf.


41. Web Scopes und Destruction

Für Request-scoped Beans:


Destruction passiert am Ende des HTTP Requests.

Für Session-scoped Beans:


Destruction passiert, wenn die HTTP Session endet.

Für Singleton Beans:


Destruction passiert, wenn der Application Context schließt.

Für Prototype Beans:


Spring verwaltet Destruction nicht automatisch.

42. Lifecycle-Reihenfolge — Zusammenfassung

Reihenfolge auf hoher Ebene:


1. Instanziieren
2. Dependencies injizieren
3. Aware Callbacks
4. BeanPostProcessor vor Initialisierung
5. Initialization Callbacks
6. BeanPostProcessor nach Initialisierung
7. Bean bereit
8. Destruction Callbacks

Beispiele für Initialization Callbacks:


@PostConstruct
InitializingBean.afterPropertiesSet()
@Bean(initMethod = "...")

Beispiele für Destruction Callbacks:


@PreDestroy
DisposableBean.destroy()
@Bean(destroyMethod = "...")

43. Praxisbeispiel: External Client

Third-Party-Klasse:


public class TaxApiClient {

public void connect() {
System.out.println("Connecting to tax API");
}

public void close() {
System.out.println("Closing tax API client");
}
}

Konfiguration:


@Configuration
public class TaxApiConfig {

@Bean(initMethod = "connect", destroyMethod = "close")
public TaxApiClient taxApiClient() {
return new TaxApiClient();
}
}

Spring Lifecycle:


1. TaxApiClient erzeugen
2. connect() aufrufen
3. TaxApiClient nutzen
4. Beim Shutdown close() aufrufen

44. Praxisbeispiel: Configuration beim Startup validieren


@Component
public class JwtConfigValidator {

private final JwtProperties jwtProperties;

public JwtConfigValidator(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}

@PostConstruct
public void validate() {
if (jwtProperties.secret().length() < 32) {
throw new IllegalStateException("JWT secret is too short");
}
}
}

Wenn die Konfiguration ungültig ist, schlägt die Application beim Startup fehl.

Das kann gut sein, weil schlechte Config früh erkannt wird.


45. Praxisbeispiel: Aktive Configuration sicher loggen


@Component
public class StartupLogger {

private final AppProperties appProperties;

public StartupLogger(AppProperties appProperties) {
this.appProperties = appProperties;
}

@PostConstruct
public void logStartupInfo() {
System.out.println("App started: " + appProperties.name());
}
}

Wichtig:


Keine Secrets loggen.

Gut zu loggen:


App-Name
aktive Feature Flags
nicht-sensitive URLs
Profile
Version

Schlecht zu loggen:


Passwörter
Tokens
JWT Secrets
API Keys
private Credentials

46. @PostConstruct und Proxies

Wichtige fortgeschrittene Prüfungsfalle:

@PostConstruct läuft während der Bean-Initialisierung.

AOP Proxy-Verhalten funktioniert in @PostConstruct möglicherweise nicht gleich.

Beispiel:


@Service
public class PaymentService {

@PostConstruct
public void init() {
pay();
}

@Transactional
public void pay() {
// transaction logic
}
}

pay() aus derselben Klasse aufzurufen ist Self-Invocation.

Der Aufruf geht möglicherweise nicht durch den Proxy.

Deshalb funktioniert @Transactional vielleicht nicht wie erwartet.

Merksatz:

Verlasse dich bei Self-Calls in @PostConstruct nicht auf proxy-basierte Annotationen.

Das schauen wir uns in AOP und Transactions genauer an.


47. @PostConstruct und Self-Invocation

Schlechtes Muster:


@PostConstruct
public void init() {
transactionalMethod();
}

Warum schlecht?


Der Aufruf ist innerhalb derselben Klasse.
Er kann den Spring Proxy umgehen.
@Transactional, @Async, @Cacheable oder Security-Annotationen greifen möglicherweise nicht.

Besser:


Nutze ApplicationRunner, Event Listener oder verschiebe die Logik in einen anderen Bean, wenn Proxy-Verhalten nötig ist.

48. Lifecycle und Lazy Beans

Standardmäßig werden Singleton Beans beim Startup eager erzeugt.

Aber wenn ein Bean lazy ist:


@Component
@Lazy
public class HeavyService {
}

erzeugt Spring ihn erst, wenn er zum ersten Mal gebraucht wird.

Dann startet sein Lifecycle zu diesem späteren Zeitpunkt.

Wichtig:


@PostConstruct läuft, wenn der Lazy Bean tatsächlich erzeugt wird.

Nicht unbedingt beim Application Startup.


49. @Lazy-Beispiel


@Component
@Lazy
public class ReportGenerator {

@PostConstruct
public void init() {
System.out.println("ReportGenerator initialized");
}
}

Wenn kein anderer Bean ReportGenerator beim Startup braucht, wird er vielleicht nicht sofort erzeugt.

Wenn er zum ersten Mal angefragt wird, erzeugt Spring ihn und führt @PostConstruct aus.


50. Typische Prüfungsfallen

Falle 1

@PostConstruct läuft nach der Dependency Injection — nicht davor.


Falle 2

@PreDestroy läuft, bevor Spring den Bean zerstört.


Falle 3

Spring ruft Destroy Callbacks für Prototype Beans nicht automatisch auf.


Falle 4

InitializingBean und DisposableBean funktionieren, aber sie koppeln Code an Spring.


Falle 5

@Bean(initMethod = "...") und @Bean(destroyMethod = "...") sind nützlich für Third-Party-Klassen.


Falle 6

BeanPostProcessor kann Beans verändern oder wrappen.


Falle 7

AOP Proxies werden oft von BeanPostProcessors erzeugt.


Falle 8

Das in einen anderen Bean injizierte Objekt kann ein Proxy sein — nicht das Originalobjekt.


Falle 9

Verlasse dich nicht auf @Transactional Self-Invocation in @PostConstruct.


Falle 10

Lazy Beans werden initialisiert, wenn sie zum ersten Mal angefragt werden — nicht unbedingt beim Startup.


51. Prüfungsfrage: @PostConstruct

Frage:

Wann läuft eine mit @PostConstruct annotierte Methode?

Antwort:

Sie läuft, nachdem der Bean erzeugt wurde und Dependencies injiziert wurden — aber bevor der Bean für den normalen Gebrauch bereit ist.


52. Prüfungsfrage: @PreDestroy

Frage:

Wann läuft eine mit @PreDestroy annotierte Methode?

Antwort:

Sie läuft, bevor der Bean zerstört wird — meist wenn der Application Context für Singleton Beans schließt.


53. Prüfungsfrage: Prototype Destruction

Frage:

Ruft Spring @PreDestroy für Prototype Beans automatisch auf?

Antwort:

Nein. Spring erzeugt und initialisiert Prototype Beans, verwaltet aber ihre Destruction nicht automatisch. Der Aufrufer ist für Cleanup verantwortlich.


54. Prüfungsfrage: InitializingBean

Frage:

Was macht InitializingBean.afterPropertiesSet()?

Antwort:

Es ist ein Initialization Callback, den Spring aufruft, nachdem Bean Properties und Dependencies gesetzt wurden.


55. Prüfungsfrage: DisposableBean

Frage:

Was macht DisposableBean.destroy()?

Antwort:

Es ist ein Destruction Callback, den Spring aufruft, wenn der Bean zerstört wird — meist wenn der Application Context schließt.


56. Prüfungsfrage: @Bean(initMethod)

Frage:


@Bean(initMethod = "connect")
public ExternalClient externalClient() {
return new ExternalClient();
}

Wann läuft connect()?

Antwort:

Spring ruft connect() auf, nachdem der Bean erzeugt und konfiguriert wurde — während der Initialisierung.


57. Prüfungsfrage: @Bean(destroyMethod)

Frage:


@Bean(destroyMethod = "close")
public ExternalClient externalClient() {
return new ExternalClient();
}

Wann läuft close()?

Antwort:

Spring ruft close() auf, wenn der Bean zerstört wird — meist wenn der Application Context schließt.


58. Prüfungsfrage: BeanPostProcessor

Frage:

Was ist ein BeanPostProcessor?

Antwort:

Ein BeanPostProcessor ist ein Spring Extension Point, der Beans vor und nach der Initialisierung verändern, inspizieren oder wrappen kann. Er kann auch einen Proxy statt des ursprünglichen Beans zurückgeben.


59. Prüfungsfrage: AOP und BeanPostProcessor

Frage:

Wie hängen AOP Proxies mit BeanPostProcessor zusammen?

Antwort:

AOP Proxies werden oft von BeanPostProcessors nach der Bean-Initialisierung erzeugt. Der Post-Processor kann den ursprünglichen Bean in einen Proxy wrappen, der Verhalten wie Transactions, Security, Caching oder Logging hinzufügt.


60. Prüfungsfrage: Lifecycle-Reihenfolge

Frage:

Was passiert zuerst: Dependency Injection oder @PostConstruct?

Antwort:

Dependency Injection passiert zuerst. @PostConstruct läuft, nachdem Dependencies injiziert wurden.


61. Prüfungsfrage: Lazy Bean

Frage:

Wann läuft @PostConstruct für einen Lazy Bean?

Antwort:

Er läuft, wenn der Lazy Bean tatsächlich erzeugt wird — das kann später als beim Application Startup sein.


62. Prüfungsfrage: Constructor vs. @PostConstruct

Frage:

Warum könnte Initialisierungslogik in @PostConstruct statt im Constructor stehen?

Antwort:

Weil @PostConstruct läuft, nachdem Spring Dependencies injiziert und mehr vom Bean Setup abgeschlossen hat. Der Constructor läuft, während das Objekt noch erzeugt wird.


63. Interview-Antwort

Frage:

What is the Spring bean lifecycle?

Gute Antwort:

Der Spring Bean Lifecycle ist der Prozess, den ein Bean von der Erzeugung bis zur Zerstörung durchläuft. Spring liest die Bean Definition, instanziiert den Bean, injiziert Dependencies, ruft Aware Callbacks auf, wendet BeanPostProcessors an, führt Initialization Callbacks wie @PostConstruct aus — und dann ist der Bean einsatzbereit. Wenn der Application Context schließt, führt Spring Destruction Callbacks wie @PreDestroy für Singleton Beans aus.


64. Interview-Antwort

Frage:

What is the difference between @PostConstruct and @PreDestroy?

Gute Antwort:

@PostConstruct markiert eine Methode, die nach der Dependency Injection und bevor der Bean genutzt wird läuft. Sie ist für Initialisierungslogik. @PreDestroy markiert eine Methode, die läuft, bevor der Bean zerstört wird — meist wenn der Application Context schließt. Sie ist für Cleanup-Logik wie Ressourcen schließen oder Background Tasks stoppen.


65. Interview-Antwort

Frage:

What is a BeanPostProcessor?

Gute Antwort:

Ein BeanPostProcessor ist ein Extension Point in Spring, der Beans vor und nach der Initialisierung verändern kann. Er kann Beans inspizieren, ändern oder mit Proxies wrappen. Viele Spring Features nutzen BeanPostProcessors intern — unter anderem für Lifecycle-Annotationen und AOP Proxy-Erzeugung.


66. Interview-Antwort

Frage:

Why are BeanPostProcessors important for AOP?

Gute Antwort:

BeanPostProcessors sind für AOP wichtig, weil sie einen Bean nach Erzeugung und Initialisierung mit einem Proxy wrappen können. Dieser Proxy kann Verhalten um Methodenaufrufe hinzufügen — wie Transactions, Security Checks, Caching oder Logging. Deshalb kann ein in einen anderen Bean injizierter Bean tatsächlich ein Proxy sein — nicht das Originalobjekt.


67. Interview-Antwort

Frage:

Does Spring manage prototype bean destruction?

Gute Antwort:

Spring erzeugt und initialisiert Prototype Beans, verwaltet aber ihren vollen Destruction Lifecycle nicht automatisch. Nachdem Spring einen Prototype Bean an den Aufrufer zurückgibt, ist der Aufrufer für Cleanup verantwortlich. Das unterscheidet sich von Singleton Beans, bei denen Spring Destruction Callbacks aufruft, wenn der Application Context schließt.


68. Kleine Code-Übung

Erstelle diesen Bean:


@Component
public class LifecycleDemoBean {

public LifecycleDemoBean() {
System.out.println("1. Constructor");
}

@PostConstruct
public void init() {
System.out.println("2. PostConstruct");
}

@PreDestroy
public void destroy() {
System.out.println("3. PreDestroy");
}
}

Erwartete Ausgabe konzeptionell:


Application startup:
1. Constructor
2. PostConstruct

Application shutdown:
3. PreDestroy

Frage:

Warum läuft @PostConstruct nach dem Constructor?

Antwort:

Weil der Constructor das Objekt zuerst erzeugt. Danach injiziert Spring Dependencies und führt Initialization Callbacks wie @PostConstruct aus.


69. Kleine Bug-Übung

Problem:


@Service
public class StartupService {

@PostConstruct
public void init() {
runInTransaction();
}

@Transactional
public void runInTransaction() {
// database logic
}
}

Frage:

Was ist das Problem?

Antwort:

init() ruft runInTransaction() innerhalb derselben Klasse auf. Das ist Self-Invocation und kann den Spring Proxy umgehen. Weil @Transactional proxy-basiert ist, wird die Transaction möglicherweise nicht wie erwartet angewendet.

Bessere Optionen:


Transactional Logic in einen anderen Bean verschieben.
ApplicationRunner nutzen.
Application Event Listener nutzen.
Proxy-basierte Self-Calls in @PostConstruct vermeiden.

Übungsfragen

Frage 1

Was ist der Bean Lifecycle?

Antwort:

Der Bean Lifecycle ist der Prozess, den Spring befolgt, um einen Bean zu erzeugen, zu konfigurieren, zu initialisieren, zu nutzen und zu zerstören.


Frage 2

Was ist die vereinfachte Lifecycle-Reihenfolge?

Antwort:

Die vereinfachte Reihenfolge ist: Bean instanziieren, Dependencies injizieren, Aware Callbacks ausführen, BeanPostProcessor vor Initialisierung ausführen, Initialization Callbacks ausführen, BeanPostProcessor nach Initialisierung ausführen, Bean nutzen und schließlich Destruction Callbacks ausführen.


Frage 3

Wann werden Dependencies injiziert?

Antwort:

Dependencies werden nach oder während der Bean-Instantiation injiziert — je nach Injection-Stil. Bei Constructor Injection werden Dependencies während der Konstruktion bereitgestellt. Bei Field- oder Setter Injection werden sie nach der Objekterzeugung injiziert.


Frage 4

Was macht @PostConstruct?

Antwort:

@PostConstruct markiert eine Methode, die Spring während der Bean-Initialisierung aufrufen soll.


Frage 5

Wann läuft @PostConstruct?

Antwort:

@PostConstruct läuft, nachdem der Bean erzeugt wurde und Dependencies injiziert wurden.


Frage 6

Was macht @PreDestroy?

Antwort:

@PreDestroy markiert eine Methode, die Spring aufrufen soll, bevor der Bean zerstört wird.


Frage 7

Wann läuft @PreDestroy?

Antwort:

@PreDestroy läuft, wenn der Bean zerstört wird — meist wenn der Application Context für Singleton Beans schließt.


Frage 8

Was ist InitializingBean?

Antwort:

InitializingBean ist ein Spring Interface mit der Methode afterPropertiesSet(), die läuft, nachdem Dependencies und Properties gesetzt wurden.


Frage 9

Was ist DisposableBean?

Antwort:

DisposableBean ist ein Spring Interface mit der Methode destroy(), die läuft, wenn der Bean zerstört wird.


Frage 10

Wann solltest du @Bean(initMethod = "...") nutzen?

Antwort:

Nutze @Bean(initMethod = "..."), wenn du einen Bean aus einer Third-Party-Klasse erzeugst oder aus einer Klasse, in der du kein @PostConstruct hinzufügen kannst — und die Klasse eine Initialisierungsmethode hat.


Frage 11

Wann solltest du @Bean(destroyMethod = "...") nutzen?

Antwort:

Nutze @Bean(destroyMethod = "..."), wenn der Bean eine Cleanup-Methode wie close(), shutdown() oder stop() hat, die laufen soll, wenn der Context schließt.


Frage 12

Was ist BeanPostProcessor?

Antwort:

BeanPostProcessor ist ein Spring Extension Point, der Beans vor und nach der Initialisierung inspizieren, verändern oder wrappen kann.


Frage 13

Was kann ein BeanPostProcessor?

Antwort:

Ein BeanPostProcessor kann Bean Properties verändern, Beans inspizieren, Beans ersetzen oder Beans mit Proxies wrappen.


Frage 14

Wie hängt BeanPostProcessor mit AOP zusammen?

Antwort:

AOP Proxies werden oft von BeanPostProcessors erzeugt. Ein Post-Processor kann einen Bean mit einem Proxy wrappen, der Verhalten wie Transactions, Security, Caching oder Logging hinzufügt.


Frage 15

Zerstört Spring Prototype Beans automatisch?

Antwort:

Nein. Spring erzeugt und initialisiert Prototype Beans, verwaltet aber ihre Destruction nicht automatisch.


Frage 16

Warum ist @PostConstruct oft besser als Initialisierung im Constructor?

Antwort:

@PostConstruct läuft nach der Dependency Injection — Dependencies sind also bereit. Der Constructor läuft, während das Objekt noch erzeugt wird.


Frage 17

Warum solltest du vorsichtig sein, @Transactional-Methoden aus @PostConstruct aufzurufen?

Antwort:

Weil der Aufruf einer anderen Methode in derselben Klasse Self-Invocation ist und den Spring Proxy umgehen kann. Proxy-basierte Features wie @Transactional greifen möglicherweise nicht.


Frage 18

Wann läuft @PostConstruct für einen Lazy Bean?

Antwort:

Bei einem Lazy Bean läuft @PostConstruct, wenn der Bean tatsächlich erzeugt wird — das kann später als beim Application Startup sein.


Frage 19

Was sind Aware Interfaces?

Antwort:

Aware Interfaces sind Spring Interfaces, die einem Bean erlauben, Spring-Infrastruktur-Objekte zu erhalten — wie seinen Bean-Namen, den ApplicationContext oder die Environment.


Frage 20

Sollte normaler Business-Code oft ApplicationContextAware nutzen?

Antwort:

Meist nein. Normaler Business-Code sollte Constructor Injection bevorzugen — statt direkt vom ApplicationContext abzuhängen.

Merksätze zum Mitnehmen

  • Bean Lifecycle bedeutet: erzeugen, injizieren, initialisieren, nutzen und zerstören.
  • @PostConstruct läuft nach der Dependency Injection.
  • @PreDestroy läuft vor der Bean-Zerstörung.
  • InitializingBean.afterPropertiesSet() ist ein Initialization Callback.
  • DisposableBean.destroy() ist ein Destruction Callback.
  • @Bean(initMethod = "...") ist nützlich für Third-Party-Initialisierung.
  • @Bean(destroyMethod = "...") ist nützlich für Third-Party-Cleanup.
  • BeanPostProcessor kann Beans vor und nach der Initialisierung verändern oder wrappen.
  • AOP Proxies werden oft von BeanPostProcessors erzeugt.
  • Ein in einen anderen Bean injizierter Bean kann tatsächlich ein Proxy sein.
  • Spring verwaltet Destruction Callbacks für Singleton Beans.
  • Spring zerstört Prototype Beans nicht automatisch.
  • Lazy Beans werden initialisiert, wenn sie zum ersten Mal angefragt werden.
  • Verlasse dich bei Self-Invocation in @PostConstruct nicht auf proxy-basiertes Verhalten.