Aller au contenu principal

Chapitre 4: Annotations de Base Spring Boot

Introduction​

Les annotations sont au cœur de Spring Boot. Elles permettent de configurer l'application de manière déclarative, sans XML, et rendent le code plus lisible et maintenable.

Objectifs du Chapitre​

À la fin de ce chapitre, vous serez capable de:

  • Comprendre le rôle des annotations principales de Spring Boot
  • Utiliser @RestController, @Service, @Repository
  • Maîtriser les annotations de mapping HTTP
  • Injecter des dépendances avec @Autowired
  • Créer des beans Spring

Qu'est-ce qu'une Annotation?​

Définition​

Une annotation est une forme de métadonnée qui fournit des informations sur le code sans en faire partie directement.

Format:

@NomAnnotation
@NomAnnotation(paramettre=valeur)

Exemple:

@RestController
@RequestMapping("/api")
public class UserController {
// ...
}

Pourquoi les Annotations?​

Avantages:

  • Code plus concis
  • Configuration proche du code
  • Type-safe (vérification à la compilation)
  • Facilite le refactoring

@SpringBootApplication​

L'Annotation Maîtresse​

package com.example.greeting;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GreetingApplication {

public static void main(String[] args) {
SpringApplication.run(GreetingApplication.class, args);
}
}

Composition de @SpringBootApplication​

@SpringBootApplication est une méta-annotation qui combine trois annotations:

@SpringBootApplication
=
@SpringBootConfiguration
+ @EnableAutoConfiguration
+ @ComponentScan

Détail de Chaque Annotation​

1. @SpringBootConfiguration​

Rôle: Marque la classe comme source de définitions de beans

Équivalent à:

@Configuration

Permet de définir des beans:

@SpringBootApplication
public class GreetingApplication {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

2. @EnableAutoConfiguration​

Rôle: Active la configuration automatique de Spring Boot

Exemple: Si Spring Boot détecte:

  • spring-boot-starter-web → Configure Tomcat, Spring MVC
  • spring-boot-starter-data-jpa → Configure Hibernate, DataSource
  • PostgreSQL Driver → Configure PostgreSQL

Configuration automatique:

3. @ComponentScan​

Rôle: Scanne le package et les sous-packages à la recherche de composants Spring

Package scanné:

package com.example.greeting;  // Package de base

// Spring scannera:
// - com.example.greeting
// - com.example.greeting.controller
// - com.example.greeting.service
// - etc.

Composants détectés:

  • @Component
  • @Controller / @RestController
  • @Service
  • @Repository
  • @Configuration

Annotations de Stéréotype​

Hiérarchie des Composants​

@Component​

Rôle: Annotation générique pour tout composant Spring

Utilisation:

import org.springframework.stereotype.Component;

@Component
public class EmailValidator {

public boolean isValid(String email) {
return email.contains("@");
}
}

Quand l'utiliser: Pour des classes utilitaires qui ne rentrent pas dans les autres catégories

@Controller​

Rôle: Marque une classe comme contrôleur MVC (retourne des vues)

Utilisation:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

@GetMapping("/")
public String home(Model model) {
model.addAttribute("message", "Bienvenue!");
return "home"; // Retourne la vue "home.html"
}
}

Architecture:

@RestController​

Rôle: Contrôleur REST (retourne des données JSON/XML)

Équivalent à:

@RestController
=
@Controller + @ResponseBody

Utilisation:

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
public class GreetingController {

@GetMapping("/greeting")
public Greeting greeting() {
return new Greeting(1L, "Hello, World!");
}
}

Architecture:

Différence @Controller vs @RestController:

Aspect@Controller@RestController
RetourNom de vue (String)Données (JSON/XML)
UsageApplications web traditionnellesREST APIs
SerializationNonAutomatique (Jackson)
@ResponseBodyRequis sur chaque méthodeImplicite

@Service​

Rôle: Marque une classe contenant la logique métier

Utilisation:

import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class GreetingService {

private final AtomicLong counter = new AtomicLong();
private static final String template = "Hello, %s!";

public Greeting createGreeting(String name) {
return new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);
}
}

Responsabilités:

  • Logique métier
  • Calculs complexes
  • Orchestration des repositories
  • Transactions

@Repository​

Rôle: Marque une classe d'accès aux données

Utilisation avec JPA:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA génère automatiquement l'implémentation
}

Responsabilités:

  • Requêtes à la base de données
  • CRUD operations
  • Queries personnalisées

Avantage: Traduction automatique des exceptions SQL en exceptions Spring


Annotations de Mapping HTTP​

Vue d'Ensemble​

@RequestMapping​

Rôle: Annotation générique pour mapper des requêtes HTTP

Syntaxe:

@RequestMapping(value = "/path", method = RequestMethod.GET)

Utilisation au niveau de la classe:

@RestController
@RequestMapping("/api/users")
public class UserController {

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable Long id) {
// GET /api/users/123
return userService.findById(id);
}
}

@GetMapping​

Rôle: Raccourci pour les requêtes GET

Équivalent à:

@GetMapping("/path")
=
@RequestMapping(value = "/path", method = RequestMethod.GET)

Exemples:

@RestController
@RequestMapping("/api")
public class ProductController {

// GET /api/products
@GetMapping("/products")
public List<Product> getAllProducts() {
return productService.findAll();
}

// GET /api/products/123
@GetMapping("/products/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.findById(id);
}

// GET /api/products?category=books
@GetMapping("/products")
public List<Product> getProductsByCategory(@RequestParam String category) {
return productService.findByCategory(category);
}
}

@PostMapping​

Rôle: Raccourci pour les requêtes POST (création)

Utilisation:

@RestController
@RequestMapping("/api/users")
public class UserController {

@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

Requête:

POST /api/users
Content-Type: application/json

{
"name": "John Doe",
"email": "john@example.com"
}

@PutMapping​

Rôle: Raccourci pour les requêtes PUT (mise à jour complète)

Utilisation:

@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}

Requête:

PUT /api/users/123
Content-Type: application/json

{
"name": "John Doe Updated",
"email": "john.updated@example.com"
}

@DeleteMapping​

Rôle: Raccourci pour les requêtes DELETE (suppression)

Utilisation:

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}

Requête:

DELETE /api/users/123

@PatchMapping​

Rôle: Raccourci pour les requêtes PATCH (mise à jour partielle)

Utilisation:

@PatchMapping("/{id}")
public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
return userService.partialUpdate(id, updates);
}

Requête:

PATCH /api/users/123
Content-Type: application/json

{
"email": "newemail@example.com"
}

Tableau Récapitulatif​

Méthode HTTPAnnotationActionIdempotent
GET@GetMappingLire✅
POST@PostMappingCréer❌
PUT@PutMappingMettre à jour (complet)✅
PATCH@PatchMappingMettre à jour (partiel)❌
DELETE@DeleteMappingSupprimer✅

Annotations de Paramètres​

@PathVariable​

Rôle: Extraire une valeur de l'URL

Syntaxe:

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// URL: /users/123
// id = 123
return userService.findById(id);
}

Plusieurs variables:

@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(
@PathVariable Long userId,
@PathVariable Long orderId
) {
return orderService.findByUserAndId(userId, orderId);
}

Nom différent:

@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long userId) {
return userService.findById(userId);
}

@RequestParam​

Rôle: Extraire des paramètres de query string

Syntaxe:

@GetMapping("/users")
public List<User> getUsers(@RequestParam String name) {
// URL: /users?name=John
// name = "John"
return userService.findByName(name);
}

Paramètre optionnel:

@GetMapping("/users")
public List<User> getUsers(
@RequestParam(required = false) String name
) {
if (name != null) {
return userService.findByName(name);
}
return userService.findAll();
}

Valeur par défaut:

@GetMapping("/users")
public List<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
return userService.findAll(page, size);
}

Plusieurs paramètres:

@GetMapping("/users")
public List<User> searchUsers(
@RequestParam String name,
@RequestParam int age,
@RequestParam String city
) {
// URL: /users?name=John&age=30&city=Montreal
return userService.search(name, age, city);
}

@RequestBody​

Rôle: Mapper le corps de la requête HTTP sur un objet Java

Utilisation:

@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.save(user);
}

Requête HTTP:

POST /users
Content-Type: application/json

{
"name": "John Doe",
"email": "john@example.com",
"age": 30
}

Objet Java reçu:

User user = new User();
user.setName("John Doe");
user.setEmail("john@example.com");
user.setAge(30);

@RequestHeader​

Rôle: Extraire un header HTTP

Utilisation:

@GetMapping("/secure")
public String secureEndpoint(
@RequestHeader("Authorization") String token
) {
// token = "Bearer eyJhbGciOiJIUzI1NiIs..."
return "Authenticated!";
}

Tableau Comparatif​

AnnotationSourceExemple URLUtilisation
@PathVariableChemin URL/users/123Identifiants de ressources
@RequestParamQuery string/users?name=JohnFiltres, pagination
@RequestBodyCorps HTTPPOST/PUT bodyEnvoi de données
@RequestHeaderHeaders HTTPAuthorization: Bearer tokenAuthentification

Injection de Dépendances​

@Autowired​

Rôle: Injecte automatiquement une dépendance

Types d'Injection​

1. Injection par Constructeur (Recommandée)​

@RestController
public class UserController {

private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}
}

Depuis Spring 4.3: @Autowired est optionnel si un seul constructeur

@RestController
public class UserController {

private final UserService userService;

// @Autowired pas nécessaire
public UserController(UserService userService) {
this.userService = userService;
}
}

Avantages:

  • Immutabilité (final)
  • Facilite les tests
  • Obligatoire (pas de NullPointerException)

2. Injection par Setter​

@RestController
public class UserController {

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}

Avantages:

  • Dépendances optionnelles
  • Reconfiguration possible

Inconvénients:

  • Mutable
  • Risque de NullPointerException

3. Injection par Champ (Non Recommandée)​

@RestController
public class UserController {

@Autowired
private UserService userService;
}

Inconvénients:

  • Difficile à tester
  • Couplage fort
  • Impossible de créer un objet immutable

Recommandation: Toujours utiliser l'injection par constructeur

Exemple Complet d'Architecture​

// Entité
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters/Setters
}

// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

// Service
@Service
public class UserService {

private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public List<User> findAll() {
return userRepository.findAll();
}

public User save(User user) {
return userRepository.save(user);
}
}

// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}

@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

Flux des dépendances:


Exercices Pratiques​

Exercice 1: Créer un Contrôleur REST​

Objectif: Utiliser @RestController et @GetMapping

Instructions:

  1. Créez une classe HelloController
  2. Annotez avec @RestController
  3. Créez une méthode sayHello() qui retourne "Hello, Spring Boot!"
  4. Annotez avec @GetMapping("/hello")
  5. Testez avec le navigateur

Exercice 2: Utiliser @PathVariable​

Objectif: Extraire des paramètres d'URL

Instructions:

  1. Créez un endpoint /greet/\{name\}
  2. Retournez "Hello, {name}!"
  3. Testez avec /greet/John

Exercice 3: Créer un Service​

Objectif: Séparer la logique métier

Instructions:

  1. Créez une classe CalculatorService avec @Service
  2. Ajoutez une méthode add(int a, int b)
  3. Créez un CalculatorController
  4. Injectez le service par constructeur
  5. Créez un endpoint /add?a=5&b=3

Exercice 4: Architecture Complète​

Objectif: Créer une architecture à 3 couches

Instructions:

  1. Créez une classe Product (model)
  2. Créez ProductService avec une liste en mémoire
  3. Créez ProductController
  4. Implémentez:
    • GET /products (tous)
    • GET /products/{id}
    • POST /products

Questions de Révision​

  1. Quelles annotations composent @SpringBootApplication?

  2. Quelle est la différence entre @Controller et @RestController?

  3. Quand utiliser @Service vs @Component?

  4. Quelle est la différence entre @PathVariable et @RequestParam?

  5. Pourquoi l'injection par constructeur est-elle recommandée?

  6. Que fait l'annotation @RequestBody?

  7. Quelle méthode HTTP correspond à @PostMapping?

  8. À quoi sert @Repository?


Résumé du Chapitre​

Points Clés​

  • @SpringBootApplication combine configuration, auto-configuration et scan
  • @RestController = @Controller + @ResponseBody
  • @Service pour la logique métier, @Repository pour l'accès aux données
  • Annotations de mapping: @GetMapping, @PostMapping, etc.
  • @PathVariable pour l'URL, @RequestParam pour les query strings
  • Injection par constructeur recommandée

Bonnes Pratiques​

  • Utiliser des annotations spécialisées (@Service, @Repository) plutôt que @Component
  • Injection par constructeur avec final
  • Un contrôleur par ressource REST
  • Séparer clairement les responsabilités (Controller → Service → Repository)
  • Nommer les endpoints de manière cohérente (REST conventions)

Prochaines Étapes​

Félicitations! Vous avez complété le Module 2.

Dans le Module 3 (à venir), vous approfondirez:

  • L'Inversion de Contrôle en détail
  • Le cycle de vie des beans
  • Les scopes
  • La configuration avancée

Mettre en Pratique​

Avant de passer au module suivant, assurez-vous de:

  • Compléter le TP2.1: Application Greeting
  • Refaire les exercices de chaque chapitre
  • Créer votre propre mini-projet pour consolider les acquis

Retour au Module 2