Spring Boot Basic – Tag 2 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting

Tag 2: Der Spring Container & Dependency Injection
Spring Boot Basic – Tag 2 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting
📍 Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| ✅ 1 | Erste REST API | Abgeschlossen |
| →2 | Spring Container & DI + CRUD | 👉 DU BIST HIER! |
| 3 | @Controller & Thymeleaf Basics | Noch nicht freigeschaltet |
| 4 | Thymeleaf Forms & MVC-Pattern | Noch nicht freigeschaltet |
| 5 | Konfiguration & Logging | Noch nicht freigeschaltet |
| 6 | DI & AOP im Detail | Noch nicht freigeschaltet |
| 7 | Scopes in Spring | Noch nicht freigeschaltet |
| 8 | WebSockets | Noch nicht freigeschaltet |
| 9 | JAX-RS in Spring Boot | Noch nicht freigeschaltet |
| 10 | Integration & Abschluss | Noch nicht freigeschaltet |
📋 Voraussetzungen
Du brauchst:
- ✅ Tag 1 abgeschlossen (REST API funktioniert)
- ✅ Spring Boot läuft auf deinem Rechner
- ✅ Grundverständnis von Interfaces in Java
- ✅ Dein Projekt von Tag 1
Tag 1 verpasst? → Hier geht’s zum Blogbeitrag Tag 1
⚡ Was du heute baust/lernst:
Gestern hast du eine REST API gebaut. Aber was passiert da eigentlich im Hintergrund? Heute schauen wir hinter die Kulissen von Spring Boot und verstehen den Spring Container – das Herz jeder Spring-Anwendung! Du lernst, wie Spring deine Objekte verwaltet und wie du Dependency Injection richtig einsetzt.
🎯 Dein Lernpfad heute:
Du arbeitest heute in mehreren aufbauenden Schwierigkeitsstufen. Arbeite in deinem eigenen Tempo durch die Schritte:
🟢 Grundlagen (Schritte 1-4)
Was du lernst:
- Den Spring Container verstehen (ApplicationContext)
- @Component, @Service, @Controller Annotations kennenlernen
- Service-Layer erstellen und einbinden
- Dependency Injection – alle drei Varianten verstehen
- Constructor Injection implementieren
Ziel: Service-Layer funktioniert, Controller nutzt Service via Constructor Injection, API läuft weiter
🟡 Professional (Schritte 5-6)
Was du lernst:
- Lombok @RequiredArgsConstructor für cleanen Code
- Spring Container debuggen (Beans auslesen)
- ContextController für Introspection
- Bean Lifecycle verstehen (@PostConstruct, @PreDestroy)
Ziel: Production-Ready Code mit Lombok, vollständiges Verständnis des Spring Containers
🔵 Bonus: Tiefes Spring-Verständnis (Schritt 7)
Was du baust/lernst:
- Verschiedene Bean Scopes ausprobieren
- Custom Beans erstellen mit @Bean
- Qualifiers bei mehreren Implementierungen
- ApplicationContext API im Detail
Ziel: Expertenwissen über Spring’s Dependency Injection Mechanismus
💡 Tipp: Die Grundlagen (Schritte 1-4) sind essenziell für alle weiteren Tage – ohne Service-Layer und DI-Verständnis wirst du nicht weiterkommen. Professional (Schritt 5-6) ist sehr empfehlenswert, besonders Lombok macht deinen Code deutlich sauberer. Die Bonus-Features kannst du überspringen, wenn du schnell zu Tag 3 willst.
💻 Los geht’s!
🟢 GRUNDLAGEN
Schritt 1: Was ist der Spring Container?
Gestern hast du das geschrieben:
@RestController
public class PersonController {
// Controller funktioniert einfach!
}
Aber WER erstellt den Controller? WER verwaltet ihn?
Antwort: Der Spring Container (auch ApplicationContext genannt)!
Der Spring Container ist wie ein Restaurant-Manager:
Du kommst ins Restaurant (startest die App) ↓ Manager (Spring Container) organisiert alles: - Koch (Service) - Kellner (Controller) ↓ Du bestellst Essen (HTTP Request) ↓ Manager koordiniert: Kellner → nimmt Bestellung Koch → bereitet zu (Service-Logik mit Daten im Speicher)
Spring Container:
- Erstellt alle Objekte (Beans)
- Verwaltet deren Lebenszyklus
- Verbindet sie miteinander (Dependency Injection)
- Räumt auf wenn’s vorbei ist
Wichtige Begriffe:
- Bean: Ein von Spring verwaltetes Objekt
- ApplicationContext: Der Spring Container selbst
- Dependency Injection (DI): Spring gibt dir die Objekte, die du brauchst
- Inversion of Control (IoC): Nicht du erstellst Objekte, Spring macht das
Schritt 2: Annotations verstehen – @Component & Co.
Spring erkennt deine Klassen durch Annotations.
Die wichtigsten Spring Annotations:
// Basis-Annotation: "Spring, bitte verwalte diese Klasse!"
@Component
public class MyComponent {
// Allgemeine Spring-Komponente
}
// Service-Layer: Business-Logik
@Service
public class PersonService {
// Rechnet, validiert, koordiniert
}
// Controller-Layer (MVC): HTTP-Handling mit Views
@Controller
public class PersonViewController {
// Gibt HTML-Views zurück (z.B. mit Thymeleaf)
}
// REST Controller-Layer: HTTP-Handling mit JSON
@RestController
public class PersonController {
// Gibt JSON zurück (kennst du schon!)
}
Was ist der Unterschied?
Technisch sind alle fast identisch – sie sagen Spring „verwalte mich!“
ABER: Die Namen zeigen die Rolle im System:
@Controller→ Gibt HTML-Views zurück@RestController→ Gibt JSON zurück (kennst du von Tag 1)@Service→ Business-Logik@Component→ Alles andere
Best Practice: Nutze die spezifischen Annotations! Das macht deinen Code lesbarer.
Schritt 3: Projekt erweitern – Service-Layer hinzufügen
Wir bauen unsere Person-API aus Tag 1 um:
Aktuell (Tag 1):
Controller → Liste im Speicher
Neu (Tag 2):
Controller → Service → Liste im Speicher
Warum ein Service-Layer?
- ✅ Trennung von Verantwortlichkeiten (Controller = HTTP, Service = Logik)
- ✅ Wiederverwendbar (mehrere Controller können denselben Service nutzen)
- ✅ Testbar (Service kann isoliert getestet werden)
- ✅ Professional (so macht man das in echten Projekten!)
3.1 PersonService erstellen
Erstelle: src/main/java/com/example/helloworldapi/service/PersonService.java
package com.example.helloworldapi.service;
import com.example.helloworldapi.model.Person;
import net.datafaker.Faker;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Service
public class PersonService {
private List<Person> persons = new ArrayList<>();
public PersonService() {
// Testdaten generieren
Faker faker = new Faker(new Locale("de"));
for (long i = 1; i <= 100; i++) {
persons.add(new Person(
i,
faker.name().firstName(),
faker.name().lastName(),
faker.internet().emailAddress()
));
}
}
public List<Person> getAllPersons() {
return persons;
}
public Person getPersonById(Long id) {
return persons.stream()
.filter(p -> p.getId().equals(id))
.findFirst()
.orElse(null);
}
public Person createPerson(Person person) {
person.setId((long) (persons.size() + 1));
persons.add(person);
return person;
}
}
Was macht @Service?
- Sagt Spring: „Das ist ein Service – verwalte ihn!“
- Spring erstellt automatisch eine Instanz beim Start
- Die Instanz ist singleton (nur eine pro App)
3.2 Controller anpassen
Jetzt muss der Controller den Service nutzen. Aber erstmal schauen wir uns an, wie das NICHT geht:
❌ FALSCH (manuell erstellen):
@RestController
public class PersonController {
private PersonService service = new PersonService(); // NICHT SO!
}
Warum falsch?
- Du umgehst den Spring Container
- Jeder Controller erstellt eigene Service-Instanz
- Ineffizient und nicht testbar
✅ RICHTIG (Dependency Injection): Spring gibt uns den Service – wir müssen ihn nur anfordern!
Schritt 4: Dependency Injection – Die drei Wege
Jetzt kommt der wichtige Teil: Wie bekommt der Controller den Service?
4.1 Field Injection (❌ Nicht empfohlen!)
@RestController
@RequestMapping("/api/persons")
public class PersonController {
@Autowired
private PersonService personService; // Spring "injiziert" hier
@GetMapping
public List<Person> getAllPersons() {
return personService.getAllPersons();
}
}
Funktioniert, aber:
- ❌ Schwer zu testen (braucht Reflection)
- ❌ Kann nicht
finalsein (nicht Thread-safe) - ❌ Versteckte Dependency
4.2 Setter Injection (🤷 Manchmal okay)
@RestController
@RequestMapping("/api/persons")
public class PersonController {
private PersonService personService;
@Autowired
public void setPersonService(PersonService personService) {
this.personService = personService;
}
@GetMapping
public List<Person> getAllPersons() {
return personService.getAllPersons();
}
}
Besser, aber:
- ✅ Testbar
- ❌ Kann immer noch nicht
finalsein - ❌ Optionale Dependency (kann null sein!)
4.3 Constructor Injection (✅ EMPFOHLEN!)
@RestController
@RequestMapping("/api/persons")
public class PersonController {
private final PersonService personService; // final = immutable!
// Spring ruft diesen Konstruktor automatisch auf
public PersonController(PersonService personService) {
this.personService = personService;
}
@GetMapping
public List<Person> getAllPersons() {
return personService.getAllPersons();
}
@GetMapping("/{id}")
public Person getPersonById(@PathVariable Long id) {
return personService.getPersonById(id);
}
}
Perfekt, weil:
- ✅
finalmöglich (immutable, Thread-safe) - ✅ Einfach testbar (Service im Test übergeben)
- ✅ Klare Dependencies (siehst du im Konstruktor)
- ✅ Spring Best Practice!
Wichtig: Seit Spring 4.3 ist @Autowired beim Konstruktor optional, wenn die Klasse nur einen Konstruktor hat!
4.4 Controller vollständig umbauen
Ersetze deinen PersonController von Tag 1 mit dieser Version:
package com.example.helloworldapi.controller;
import com.example.helloworldapi.model.Person;
import com.example.helloworldapi.service.PersonService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/persons")
public class PersonController {
private final PersonService personService;
// Constructor Injection - Spring findet PersonService automatisch
public PersonController(PersonService personService) {
this.personService = personService;
}
@GetMapping
public List<Person> getAllPersons() {
return personService.getAllPersons();
}
@GetMapping("/{id}")
public Person getPersonById(@PathVariable Long id) {
return personService.getPersonById(id);
}
@PostMapping
public Person createPerson(@RequestBody Person person) {
return personService.createPerson(person);
}
}
Teste es:
mvn spring-boot:run curl http://localhost:8080/api/persons curl http://localhost:8080/api/persons/1
🎉 Es funktioniert! Spring hat automatisch PersonService in PersonController injiziert!
🟡 PROFESSIONAL
Schritt 5: Lombok – Noch sauberer mit @RequiredArgsConstructor
Constructor Injection ist super, aber wir können es noch besser machen!
5.1 Lombok Dependency hinzufügen
In pom.xml einfügen:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
In NetBeans:
- Rechtsklick auf Projekt → Clean and Build
5.2 Controller mit Lombok
Vorher (manuell):
@RestController
@RequestMapping("/api/persons")
public class PersonController {
private final PersonService personService;
public PersonController(PersonService personService) {
this.personService = personService;
}
// ... methods ...
}
Nachher (mit Lombok):
@RestController
@RequestMapping("/api/persons")
@RequiredArgsConstructor // Lombok generiert Konstruktor automatisch!
public class PersonController {
private final PersonService personService;
// Konstruktor wird automatisch generiert!
@GetMapping
public List<Person> getAllPersons() {
return personService.getAllPersons();
}
@GetMapping("/{id}")
public Person getPersonById(@PathVariable Long id) {
return personService.getPersonById(id);
}
}
Was macht @RequiredArgsConstructor?
- Generiert automatisch einen Konstruktor
- Für alle
finalFields - Spring nutzt diesen Konstruktor für DI
Vorteil:
- ✅ Weniger Boilerplate-Code
- ✅ Bei neuen Dependencies automatisch angepasst
- ✅ Standard in modernen Spring-Projekten
Schritt 6: Spring Container debuggen
Wie viele Beans hat Spring erstellt? Welche genau?
Lass uns einen Debug-Controller bauen!
6.1 ContextController erstellen
Erstelle: src/main/java/com/example/helloworldapi/controller/ContextController.java
package com.example.helloworldapi.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/context")
public class ContextController {
private final ApplicationContext context;
public ContextController(ApplicationContext context) {
this.context = context;
}
@GetMapping("/beans")
public List<String> getAllBeans() {
// Alle Beans im Context auslesen
return Arrays.stream(context.getBeanDefinitionNames())
.sorted()
.collect(Collectors.toList());
}
@GetMapping("/my-beans")
public List<String> getMyBeans() {
// Nur unsere eigenen Beans
return Arrays.stream(context.getBeanDefinitionNames())
.filter(name -> name.contains("person") ||
name.contains("hello") ||
name.contains("context"))
.sorted()
.collect(Collectors.toList());
}
@GetMapping("/bean-count")
public int getBeanCount() {
return context.getBeanDefinitionNames().length;
}
}
6.2 Testen:
# Starte die App mvn spring-boot:run # Alle Beans anzeigen (>100!) curl http://localhost:8080/api/context/beans # Nur unsere Beans curl http://localhost:8080/api/context/my-beans # Wie viele Beans insgesamt? curl http://localhost:8080/api/context/bean-count
Ergebnis von /my-beans:
[ "contextController", "helloController", "personController", "personService" ]
🎉 Spring hat all unsere Klassen automatisch erstellt!
Ergebnis von /bean-count:
137
Spring Boot hat 137 Beans erstellt – automatisch! Das sind alle Spring Boot Auto-Konfigurationen (Tomcat, Jackson, etc.)
Schritt 7: Bean-Lifecycle verstehen
Wann erstellt Spring die Beans?
Erweitere PersonService mit Lifecycle-Methoden:
package com.example.helloworldapi.service;
import com.example.helloworldapi.model.Person;
import net.datafaker.Faker;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Service
public class PersonService {
private List<Person> persons = new ArrayList<>();
public PersonService() {
System.out.println("1️⃣ PersonService Konstruktor wird aufgerufen");
}
@PostConstruct // Wird NACH dem Konstruktor aufgerufen
public void init() {
System.out.println("2️⃣ @PostConstruct - Service ist bereit!");
// Testdaten generieren
Faker faker = new Faker(new Locale("de"));
for (long i = 1; i <= 100; i++) {
persons.add(new Person(
i,
faker.name().firstName(),
faker.name().lastName(),
faker.internet().emailAddress()
));
}
System.out.println("3️⃣ " + persons.size() + " Personen generiert");
}
@PreDestroy // Wird BEIM Shutdown aufgerufen
public void cleanup() {
System.out.println("🔴 @PreDestroy - PersonService wird aufgeräumt!");
}
public List<Person> getAllPersons() {
return persons;
}
public Person getPersonById(Long id) {
return persons.stream()
.filter(p -> p.getId().equals(id))
.findFirst()
.orElse(null);
}
public Person createPerson(Person person) {
person.setId((long) (persons.size() + 1));
persons.add(person);
return person;
}
}
Beim Start siehst du:
1️⃣ PersonService Konstruktor wird aufgerufen 2️⃣ @PostConstruct - Service ist bereit! 3️⃣ 100 Personen generiert ... Tomcat started on port(s): 8080 (http)
Beim Shutdown (Ctrl+C):
🔴 @PreDestroy - PersonService wird aufgeräumt!
Bean Lifecycle:
- Konstruktor wird aufgerufen
- Dependencies werden injected
@PostConstructMethoden ausführen- Bean ist bereit für Nutzung
- …App läuft…
@PreDestroyMethoden ausführen- Bean wird zerstört
🔵 BONUS: TIEFES SPRING-VERSTÄNDNIS
Schritt 8: Mehrere Service-Implementierungen
Was wenn du zwei verschiedene PersonServices hast?
8.1 Interface erstellen
Erstelle: src/main/java/com/example/helloworldapi/service/PersonServiceInterface.java
package com.example.helloworldapi.service;
import com.example.helloworldapi.model.Person;
import java.util.List;
public interface PersonServiceInterface {
List<Person> getAllPersons();
Person getPersonById(Long id);
Person createPerson(Person person);
}
8.2 Zwei Implementierungen
Benenne PersonService um zu DatabasePersonService:
@Service("databaseService")
public class DatabasePersonService implements PersonServiceInterface {
// ... wie vorher ...
}
Erstelle zweiten Service:
package com.example.helloworldapi.service;
import com.example.helloworldapi.model.Person;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service("mockService")
public class MockPersonService implements PersonServiceInterface {
@Override
public List<Person> getAllPersons() {
List<Person> persons = new ArrayList<>();
persons.add(new Person(1L, "Test", "User", "test@example.com"));
return persons;
}
@Override
public Person getPersonById(Long id) {
return new Person(id, "Test", "User", "test@example.com");
}
@Override
public Person createPerson(Person person) {
return person;
}
}
8.3 Controller mit @Qualifier
Problem: Welchen Service soll Spring injizieren?
@RestController
@RequestMapping("/api/persons")
@RequiredArgsConstructor
public class PersonController {
@Qualifier("databaseService") // Oder "mockService"
private final PersonServiceInterface personService;
// ... methods ...
}
Alternative mit @Primary:
@Service
@Primary // Dieser wird standardmäßig genommen
public class DatabasePersonService implements PersonServiceInterface {
// ...
}
Bonus-Aufgaben
Weitere Ideen zum Üben:
- Eigenen Service erstellen:
StatisticsServiceder Statistiken über Personen berechnet- Anzahl Personen, häufigste Namen, etc.
- Service in Service:
PersonServicenutztValidationService- Validierung von Email-Adressen
- ApplicationContext API erforschen:
context.getBean()nutzen- Beans nach Typ suchen
- Bean-Details auslesen
- Eigenen @Bean erstellen:
@Configuration public class AppConfig { @Bean public Faker faker() { return new Faker(new Locale("de")); } }
Probier dich aus! Das ist deine Spielwiese. 🎮
Offizielle Dokumentation von Spring Framework zum Thema DI: „Dependency Injection :: Spring Framework” – erklärt das Prinzip von DI, inkl. Konstruktor- und Setter‐Injection. Home
Artikel bei Baeldung: „Spring Dependency Injection Series” – führt Schritt für Schritt in DI in Spring ein, inkl. Annotationen, Komponentenscanning, Entkopplung. Baeldung on Kotlin
Tutorial bei GeeksforGeeks: „Spring Dependency Injection with Example” – praxisorientiert mit Codebeispielen zu Konstruktor‐ und Setter‐Injection. GeeksforGeeks
Artikel bei Baeldung: „Inversion of Control and Dependency Injection in Spring” – behandelt die Grundidee von IoC & DI und wie Spring diese umsetzt. Baeldung on Kotlin
Tutorial bei DigitalOcean: „Spring Dependency Injection” – mit Beispielen zu annotierter und XML‐Konfiguration, Testbarkeit, Bean‐Scopes. digitalocean.com
✅ Checkpoint: Hast du Tag 2 geschafft?
Grundlagen (🟢):
- [ ] PersonService erstellt (@Service)
- [ ] PersonController nutzt PersonService (Constructor Injection)
- [ ]
finalbeim Service im Controller - [ ] GET /api/persons funktioniert weiterhin
- [ ] Du verstehst den Unterschied zwischen Field/Setter/Constructor Injection
Professional (🟡):
- [ ] Lombok Dependency hinzugefügt
- [ ] @RequiredArgsConstructor im Controller verwendet
- [ ] ContextController erstellt
- [ ] /api/context/my-beans zeigt deine Beans
- [ ] Bean Lifecycle mit @PostConstruct/@PreDestroy verstanden
Bonus (🔵):
- [ ] Interface für PersonService erstellt
- [ ] Mehrere Service-Implementierungen ausprobiert
- [ ] @Qualifier oder @Primary verwendet
Alles ✅? Du bist bereit für Tag 3!
Nicht alles funktioniert?
- PersonService nicht gefunden? Prüfe ob @Service Annotation da ist
- Injection funktioniert nicht? Prüfe ob Service im gleichen Package oder Sub-Package
- Lombok Error? Clean and Build ausführen
- Mehrere Beans Error? @Primary oder @Qualifier verwenden
🔥 Elyndras Real Talk:
Beim Frühstück heute wurde es lebhaft. Marcus hatte eine Frage, und plötzlich war die ganze Runde dabei.
Marcus: „Warum macht Spring das alles automatisch? Früher haben wir doch Objekte selbst erstellt!“
Franz-Martin (unser Legacy-Experte) lehnte sich zurück: „Ah, die guten alten Zeiten! 2005, Java EE 5, da haben wir noch…“
Bernd unterbrach ihn grinsend: „Franz-Martin, bitte nicht die ‚früher war alles besser‘ Geschichte!“
Aber Franz-Martin hatte einen Punkt. Früher sah Code wirklich so aus:
// 2005 - Java EE ohne Spring - pure Legacy!
public class PersonController {
private PersonService service;
public PersonController() {
// Alles manuell instanziieren!
PersonRepository repo = new PersonRepository();
DatabaseConnection conn = new DatabaseConnection("jdbc:mysql://...");
repo.setConnection(conn);
this.service = new PersonService(repo);
}
}
Franz-Martin: „Und das war in JEDEM Controller! Stellt euch vor – 50 Controller, alle mit dem gleichen Boilerplate!“
Nova (unsere Junior-Entwicklerin): „Das klingt furchtbar! Und wenn sich was ändert?“
Franz-Martin: „Genau! Wenn die DatabaseConnection einen neuen Parameter brauchte, musstest du ALLE 50 Controller anfassen!“
Bernd (lacht): „Und dann kam Spring und hat gesagt: ‚Kinder, lasst mal Papa das machen!'“
Marcus: „Aber ist das nicht… kontrollverlust? Ich erstelle doch gerne meine eigenen Objekte!“
Ich (Elyndra): „Das dachte ich am Anfang auch. Aber schau dir den Unterschied an:“
Ohne Spring (Legacy):
public class PersonController {
private PersonService service;
public PersonController() {
PersonRepository repo = new PersonRepository();
this.service = new PersonService(repo);
}
}
public class OrderController {
private PersonService service;
public OrderController() {
PersonRepository repo = new PersonRepository();
this.service = new PersonService(repo); // NEUE Instanz!
}
}
Probleme:
- ❌ Jeder Controller erstellt eigene Service-Instanz (ineffizient!)
- ❌ Jeder Service erstellt eigene Repository-Instanz
- ❌ 50 Controller = 50 Service-Instanzen im RAM
- ❌ Schwer zu testen (wie mocke ich Abhängigkeiten?)
- ❌ Änderungen im Service = Änderungen in ALLEN Controllern
- ❌ Kein zentrales Management
- ❌ Circular Dependencies? Viel Spaß beim Debuggen!
Mit Spring:
@RestController
@RequiredArgsConstructor
public class PersonController {
private final PersonService service; // Spring kümmert sich drum!
}
@RestController
@RequiredArgsConstructor
public class OrderController {
private final PersonService service; // GLEICHE Instanz wie PersonController!
}
Vorteile:
- ✅ Spring erstellt eine Service-Instanz (Singleton)
- ✅ Alle Controller teilen sich diese Instanz (effizient!)
- ✅ Einfach zu testen (Service-Mock injizieren)
- ✅ Änderungen zentral (nur im Service)
- ✅ Spring managed den Lifecycle (erstellen, aufräumen)
- ✅ Spring löst Dependencies automatisch
Franz-Martin: „Wir hatten damals auch das Problem mit Circular Dependencies. PersonService braucht OrderService, OrderService braucht PersonService – endlose NullPointerExceptions!“
Bernd: „Und heute?“
Franz-Martin (lächelt): „Spring merkt das beim Start und wirft einen klaren Fehler: ‚Hey, du hast einen Zyklus!‘ Das haben wir früher oft erst in Production gemerkt.“
Nova: „Also ist Inversion of Control wie… die Klasse gibt Verantwortung ab?“
Ich (Elyndra): „Genau! Das nennt man Inversion of Control (IoC):
Nicht du erstellst Objekte – Spring macht das für dich!“
Marcus (zu Eomma): „Das ist wie ein guter Werkzeugkoffer, Eomma. Du brauchst ihn nicht selbst zu bauen – du nutzt ihn einfach und konzentrierst dich aufs Häuser bauen!“
Bernd (lacht): „Perfekte Analogie! Spring ist der Mixer für Java-Objekte!“
Franz-Martin: „Und wer XML-Konfiguration aus Java EE 5 kennt, weiß Spring Boot zu schätzen. Früher hatten wir 500-Zeilen XML-Dateien nur um drei Beans zu konfigurieren!“
Nova: „500 Zeilen XML? Ernsthaft?“
Franz-Martin: „Oh ja. Und wenn ein Tippfehler drin war, hat die App erst beim Start gemerkt – nach 2 Minuten Bootzeit!“
Ich (Elyndra): „Heute macht Spring Boot 99% automatisch. Das ist Evolution.“
Marcus: „Und wie würdet ihr das Franz-Martin erklären, wenn er 2005 geblieben wäre?“
Bernd: „Franz-Martin würde sagen: ‚Alte Werkzeuge haben ihren Platz in der Geschichte, aber neue machen die Arbeit leichter!'“
Franz-Martin (lacht): „Genau das würde ich sagen! Und ich sage es auch heute noch. Legacy hat uns viel gelehrt – aber ich gehe nicht zurück!“
Das ist Spring Boot. Container Management, Dependency Injection, Auto-Configuration – alles automatisch, damit du dich aufs Wichtige konzentrieren kannst: deine Business-Logik!
Marcus‘ Fazit: „Also kontrollverlust ist eigentlich kontrolle durch besseres Management?“
Ich (Elyndra): „Exakt. Du gibst die Kontrolle über das ‚Wie‘ ab (Objekt-Erstellung) und behältst die Kontrolle über das ‚Was‘ (deine Business-Logik).“
Bernd: „Und genau deshalb liebe ich Spring seit 15 Jahren!“
Franz-Martin: „Seit 20 Jahren – ich war beim ersten Release dabei!“ (alle lachen)
Nova: „Eine Frage noch – wenn Spring alles managed, wie debugge ich dann, was Spring eigentlich erstellt hat?“
Ich (Elyndra): „Gute Frage! Deshalb haben wir heute den ContextController gebaut. Damit kannst du sehen, was Spring alles gemacht hat:“
curl http://localhost:8080/api/context/my-beans # ["contextController", "helloController", "personController", "personService"] curl http://localhost:8080/api/context/bean-count # 137
Nova (Augen weit): „137 Beans? Spring hat 137 Objekte erstellt?“
Bernd: „Willkommen in der Spring Boot Auto-Configuration! Tomcat, Jackson, alle Request-Handler, Error-Handler, alles automatisch!“
Franz-Martin: „Früher mussten wir jedes dieser 137 Objekte manuell in XML konfigurieren!“
Marcus: „Und wenn ich mehr Kontrolle will?“
Ich (Elyndra): „Dann überschreibst du die Auto-Configuration. Spring sagt: ‚Ich mache das für dich, aber wenn du es anders willst, bitte sehr!‘ Mit @Primary oder @Qualifier kannst du genau steuern, welche Bean verwendet wird.“
Bernd: „Eomma, du verstehst Software besser als die meisten Entwickler!“ (alle lachen)
Das ist die Macht von Spring Boot:
- Du bekommst 137 vorkonfigurierte Beans geschenkt
- Du schreibst nur noch deine Business-Logik
- Und wenn du was anders willst? Kein Problem, du überschreibst es einfach
Franz-Martin: „Wer Legacy Java EE kennt, versteht warum ich Spring liebe. Rod Johnson hat 2004 die Java-Welt verändert.“
Marcus: „Rod Johnson?“
Franz-Martin: „Der Erfinder von Spring Framework. Sein Buch ‚Expert One-on-One J2EE Design and Development‘ war revolutionär. Er sagte: ‚Java EE ist zu kompliziert‘ und baute Spring.“
Bernd: „Und 20 Jahre später bauen wir immer noch damit!“
Ich (Elyndra): „Weil gute Konzepte zeitlos sind. Dependency Injection, IoC, das sind keine Buzzwords – das sind bewährte Patterns.“
Nova: „Und für mich als Junior ist das perfekt – ich konzentriere mich auf Logik, nicht auf Objekt-Management!“
Marcus nickt: „Okay, ich bin überzeugt. Spring ist nicht Kontrollverlust, sondern Delegation an einen Experten.“
Franz-Martin: „Wie bei einem guten Werkzeugkoffer!“
Eomma: „Wie bei meinem Mixer!“
Alle lachen.
Genau das ist Spring Boot. 🚀
❓ FAQ (Häufige Fragen)
Q: Was ist der Unterschied zwischen @Component und @Service?
A: Technisch: Fast keiner. Semantisch: @Service zeigt „das ist Business-Logik“, @Component ist generisch. Nutze @Service für Klarheit!
Q: Kann ich mehrere Instanzen einer Bean haben?
A: Ja! Mit @Scope("prototype"). Default ist aber Singleton (eine Instanz für alle).
Q: Warum ist Field Injection schlecht?
A: 1) Nicht testbar ohne Spring, 2) Kann nicht final sein, 3) Versteckte Dependencies. Constructor Injection ist immer besser!
Q: Brauche ich @Autowired beim Konstruktor?
A: Nein! Seit Spring 4.3 ist @Autowired optional, wenn die Klasse nur einen Konstruktor hat.
Q: Was wenn ich zwei PersonServices habe?
A: Spring wirft einen Fehler! Lösung: @Primary auf einem Service oder @Qualifier("serviceName") beim Injizieren.
Q: Kann ich ApplicationContext direkt injizieren?
A: Ja! Siehe ContextController – das ist nützlich für Debugging.
Q: Marcus meinte „früher war das anders“ – wie war das?
A: Früher (Java EE, 2000-2010) musstest du alles in XML konfigurieren. Spring Boot macht 99% automatisch. Das ist Evolution, und wie Marcus sagen würde: „Alte Werkzeuge haben ihren Platz in der Geschichte, aber neue machen die Arbeit leichter.“
📅 Nächster Kurstag: Tag 3
Morgen im Kurs / Nächster Blogbeitrag:
„Thymeleaf & HTML – Webseiten statt JSON“
Was du lernen wirst:
- Unterschied @RestController vs. @Controller
- Thymeleaf Template Engine Setup
- Model & View Konzept verstehen
- Erste HTML-Seite mit dynamischen Daten
- th:text, th:each, th:href nutzen
- PersonService in HTML-Views verwenden
Warum wichtig? Morgen wechselst du von JSON zu echten Webseiten! Du baust dieselbe Person-API, aber mit HTML statt JSON-Response. Dein PersonService wird wiederverwendet – das ist die Macht der Service-Architektur!
Voraussetzung: Tag 2 abgeschlossen (Service-Layer funktioniert)
👉 Zum Blogbeitrag Tag 3 (erscheint morgen)
📍 Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| ✅ 1 | Erste REST API | Abgeschlossen |
| ✅ 2 | Spring Container & DI + CRUD | 👉 DU BIST HIER! |
| 3 | @Controller & Thymeleaf Basics | Noch nicht freigeschaltet |
| 4 | Thymeleaf Forms & MVC-Pattern | Noch nicht freigeschaltet |
| 5 | Konfiguration & Logging | Noch nicht freigeschaltet |
| 6 | DI & AOP im Detail | Noch nicht freigeschaltet |
| 7 | Scopes in Spring | Noch nicht freigeschaltet |
| 8 | WebSockets | Noch nicht freigeschaltet |
| 9 | JAX-RS in Spring Boot | Noch nicht freigeschaltet |
| 10 | Integration & Abschluss | Noch nicht freigeschaltet |
Du hast 20% des Kurses geschafft! 💪
Alle Blogbeiträge dieser Serie:
👉 Spring Boot Basic – Komplette Übersicht
📥 Download & Ressourcen
Projekt zum Download:
👉 tag2-spring-boot-basic-v1.0.zip (Stand: 23.10.2025)
Was ist im ZIP enthalten:
- ✅ Komplettes Maven-Projekt von Tag 1+2
- ✅ PersonService implementiert
- ✅ Constructor Injection im Controller
- ✅ ContextController zum Debuggen
- ✅ Lombok @RequiredArgsConstructor Beispiele
- ✅ Bean Lifecycle Beispiele
- ✅ Umfangreiche README mit allen Dokumentationen
- ✅ Code-Kommentare auf Deutsch
Projekt starten:
# ZIP entpacken # In NetBeans öffnen: File → Open Project # Oder im Terminal: cd springcontainerdi mvn spring-boot:run # Testen: curl http://localhost:8080/api/persons curl http://localhost:8080/api/context/my-beans
Probleme? Issue melden auf GitHub oder schreib mir: elyndra@java-developer.online
Das war Tag 2 von Spring Boot Basic!
Du kannst jetzt:
- ✅ Spring Container verstehen und erklären
- ✅ @Component, @Service, @Controller richtig einsetzen
- ✅ Dependency Injection (alle drei Varianten)
- ✅ Constructor Injection als Best Practice nutzen
- ✅ Lombok @RequiredArgsConstructor verwenden
- ✅ Spring Container debuggen (Beans auslesen)
- ✅ Bean Lifecycle mit @PostConstruct/@PreDestroy
Morgen lernst du Thymeleaf für HTML-Views! 🚀
Keep coding, keep learning!
🖖 Live long and prosper
📧 Email: elyndra.valen@java-developer.online
🌐 Website: https://www.java-developer.online
💻 GitHub: https://github.com/ElyndraValen
Tag 3 erscheint morgen. Bis dahin: Happy Coding!
Tags: #SpringBoot #DependencyInjection #Container #Java #Tag2 #Lombok #Service #IoC

