Der Component Scan arbeitet im Verborgenen. In dieser Übung machst du ihn sichtbar – und testest, was passiert, wenn Komponenten nicht gefunden werden.
Spring Boot Actuator zeigt alle registrierten Beans. Aktiviere ihn.
Schritt 1: Dependency hinzufügen
In build.gradle.kts, im
dependencies-Block:
implementation("org.springframework.boot:spring-boot-starter-actuator")Schritt 2: Endpoint freischalten
In application.yaml:
management:
endpoints:
web:
exposure:
include: beans, health, infoSchritt 3: Anwendung starten und Endpoint aufrufen
./gradlew bootRunÖffne im Browser oder mit curl:
curl http://localhost:8080/actuator/beans | jq '.contexts.application.beans | keys[]' | grep -i arcadeDu siehst alle Beans aus dem Arcade-Package:
"arcadeHighscoreApplication"
"gameService"
"homeController"
"inMemoryGameRepository"
Spring hat sie alle gefunden und registriert.
Was passiert, wenn eine Komponente nicht gefunden wird?
Schritt 1: Annotation entfernen
Öffne InMemoryGameRepository.java und kommentiere die
Annotation aus:
// @Repository <- auskommentiert
public class InMemoryGameRepository implements GameRepository {
// ...
}Schritt 2: Neu starten
./gradlew bootRunDie Anwendung startet nicht:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in de.digitalfrontiers.arcade.service.GameService
required a bean of type 'de.digitalfrontiers.arcade.repository.GameRepository'
that could not be found.
Action:
Consider defining a bean of type
'de.digitalfrontiers.arcade.repository.GameRepository' in your configuration.
Spring findet keine Implementierung von GameRepository.
Der GameService kann nicht erzeugt werden.
Schritt 3: Annotation wiederherstellen
@Repository // wieder aktiv
public class InMemoryGameRepository implements GameRepository {Die Anwendung startet wieder.
Jetzt testen wir, was außerhalb des Scan-Bereichs passiert.
Schritt 1: Neues Package außerhalb erstellen
Erstelle ein Package de.digitalfrontiers.common
(Achtung: nicht unter arcade):
src/main/java/
├── de/digitalfrontiers/
│ ├── arcade/ <- wird gescannt
│ │ └── ...
│ └── common/ <- wird NICHT gescannt
│ └── LoggingHelper.java
Schritt 2: Komponente erstellen
src/main/java/de/digitalfrontiers/common/LoggingHelper.java:
package de.digitalfrontiers.common;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
@Component
public class LoggingHelper {
@PostConstruct
public void init() {
System.out.println("=== LoggingHelper wurde initialisiert ===");
}
public void log(String message) {
System.out.println("[LOG] " + message);
}
}Schritt 3: Starten und beobachten
./gradlew bootRunSuch in der Konsolenausgabe nach
LoggingHelper wurde initialisiert. Du wirst es nicht
finden. Die Klasse liegt außerhalb des Scan-Bereichs.
Schritt 4: Scan erweitern
Ändere die Hauptklasse:
@SpringBootApplication
@ComponentScan(basePackages = {
"de.digitalfrontiers.arcade",
"de.digitalfrontiers.common"
})
public class ArcadeHighscoreApplication {
// ...
}Starte neu. Jetzt erscheint die Meldung:
=== LoggingHelper wurde initialisiert ===
Schritt 5: Aufräumen
Entferne @ComponentScan wieder und lösche das
common-Package. Wir brauchen es nicht weiter.
Spring kann detailliert protokollieren, was es beim Scan findet.
In application.yaml:
logging:
level:
org.springframework.context.annotation: DEBUGBeim Start siehst du jetzt:
DEBUG ClassPathBeanDefinitionScanner - Identified candidate component class:
file [.../arcade/service/GameService.class]
DEBUG ClassPathBeanDefinitionScanner - Identified candidate component class:
file [.../arcade/repository/InMemoryGameRepository.class]
DEBUG ClassPathBeanDefinitionScanner - Identified candidate component class:
file [.../arcade/web/HomeController.class]
Spring listet jede gefundene Komponente auf.
Entferne das Debug-Logging nach dem Experiment – es produziert viel Output.