17 Die Stereotypen-Annotationen

Spring kennt mehrere Annotationen, um Klassen als Komponenten zu markieren. Sie heißen Stereotypen, weil sie die Rolle einer Klasse beschreiben.

17.1 Die Familie

Alle Stereotypen haben denselben Effekt: Die Klasse wird als Bean registriert. Aber sie kommunizieren unterschiedliche Absichten.

@Service, @Repository und @Controller sind Spezialisierungen von @Component. Technisch sind sie austauschbar – semantisch nicht.

17.2 @Component

Die generische Annotation. Verwende sie, wenn keine der spezifischeren passt.

@Component
public class ScoreCalculator {
    
    public int calculateBonus(int baseScore, int multiplier) {
        return baseScore * multiplier;
    }
}

Ein ScoreCalculator ist weder Service noch Repository noch Controller. Er ist eine Hilfskomponente. @Component passt.

17.3 @Service

Markiert Klassen mit Geschäftslogik. Services orchestrieren Abläufe und implementieren fachliche Regeln.

@Service
public class HighscoreService {
    
    private final ScoreRepository scoreRepository;
    private final PlayerRepository playerRepository;
    
    public HighscoreService(ScoreRepository scoreRepository,
                           PlayerRepository playerRepository) {
        this.scoreRepository = scoreRepository;
        this.playerRepository = playerRepository;
    }
    
    public LeaderboardEntry getTopPlayer(String gameId) {
        Score topScore = scoreRepository.findTopByGameId(gameId);
        Player player = playerRepository.findById(topScore.getPlayerId())
            .orElseThrow();
        return new LeaderboardEntry(player.getNickname(), topScore.getPoints());
    }
}

Der Service kennt keine HTTP-Details, keine Datenbank-Queries. Er arbeitet mit Abstraktionen und implementiert die Fachlogik.

17.4 @Repository

Markiert Klassen für den Datenzugriff. Repositories kapseln die Persistenz.

@Repository
public class InMemoryScoreRepository implements ScoreRepository {
    
    private final Map<String, List<Score>> scoresByGame = new HashMap<>();
    
    @Override
    public Score findTopByGameId(String gameId) {
        return scoresByGame.getOrDefault(gameId, List.of()).stream()
            .max(Comparator.comparing(Score::getPoints))
            .orElse(null);
    }
    
    @Override
    public void save(Score score) {
        scoresByGame
            .computeIfAbsent(score.getGameId(), k -> new ArrayList<>())
            .add(score);
    }
}

@Repository hat einen Zusatzeffekt: Spring übersetzt datenbankspezifische Exceptions in einheitliche Spring-Exceptions. Bei In-Memory-Implementierungen irrelevant, bei echten Datenbanken nützlich.

17.5 @Controller

Markiert Klassen der Web-Schicht. Controller nehmen HTTP-Requests entgegen und liefern Antworten.

@Controller
public class HomeController {
    
    private final GameService gameService;
    
    public HomeController(GameService gameService) {
        this.gameService = gameService;
    }
    
    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("games", gameService.getAllGames());
        return "index";  // View-Name
    }
}

Ein @Controller arbeitet mit Views. Die Methode gibt einen View-Namen zurück, Spring rendert das passende Template.

17.6 @RestController

Eine Kombination aus @Controller und @ResponseBody. Für REST-APIs, die JSON zurückgeben.

@RestController
@RequestMapping("/api/games")
public class GameApiController {
    
    private final GameService gameService;
    
    public GameApiController(GameService gameService) {
        this.gameService = gameService;
    }
    
    @GetMapping
    public List<Game> getAllGames() {
        return gameService.getAllGames();  // Wird zu JSON
    }
    
    @GetMapping("/{id}")
    public Game getGame(@PathVariable String id) {
        return gameService.getGame(id);  // Wird zu JSON
    }
}

Kein View-Name, kein Template. Die Rückgabewerte werden direkt als JSON serialisiert.

17.7 Wann welche Annotation?

Schicht Annotation Typische Aufgaben
Web (HTML) @Controller Requests verarbeiten, Views rendern
Web (API) @RestController JSON-Endpoints bereitstellen
Logik @Service Geschäftsregeln, Orchestrierung
Daten @Repository CRUD, Queries, Persistenz
Sonstiges @Component Hilfsklassen, Utilities

Die Zuordnung ist nicht willkürlich. Sie spiegelt die Schichtenarchitektur wider:

17.8 Warum die Unterscheidung?

Technisch könntest du alles mit @Component annotieren. Der Code würde funktionieren. Aber:

Lesbarkeit: @Service sagt sofort, was die Klasse tut. @Component sagt nur, dass sie eine Bean ist.

Tooling: IDEs und Analyse-Tools nutzen die Stereotypen. Sie zeigen Services anders an als Repositories, warnen bei falscher Schichtzuordnung.

Zusatzfunktionen: @Repository aktiviert Exception-Translation. @Controller aktiviert Web-spezifisches Verhalten. @Component ist neutral.

Team-Kommunikation: In Code-Reviews ist sofort klar, welche Rolle eine Klasse spielt.

Die richtige Annotation ist wie der richtige Klassenname: technisch egal, praktisch wichtig.