CHAPITRE 5 : L'INJECTION DE DEPENDANCES

C'est le mecanisme central de Spring. Une fois compris, tout le reste devient limpide.

1. Le probleme

public class EtudiantController {

    // le controleur fabrique lui-meme ses dependances
    private EtudiantService service = new EtudiantService(new EtudiantRepository());
}

Ce code fonctionne, mais il pose trois problemes : le controleur est fortement couple a une implementation precise, il est difficile a tester (impossible de substituer un faux service), et la moindre modification du constructeur se propage partout.

2. La solution : l'inversion de controle

Vous ne creez plus vos objets : vous les demandez, et Spring les fournit.

@RestController
public class EtudiantController {

    private final EtudiantService service;

    public EtudiantController(EtudiantService service) {
        this.service = service;
    }
}

Spring voit le constructeur, cherche un EtudiantService dans son conteneur, et le passe automatiquement. C'est l'injection par constructeur, la forme recommandee.

3. Qu'est-ce qu'un bean ?

Un bean est simplement un objet gere par Spring : c'est lui qui le cree, le configure et le fournit a ceux qui en ont besoin.

AnnotationA poser sur
@ComponentUn composant generique
@ServiceUne classe de logique metier
@RepositoryUne classe d'acces aux donnees
@ControllerUn controleur web (pages)
@RestControllerUn controleur REST (JSON)

Techniquement, @Service et @Repository sont des @Component. Les distinguer rend simplement le role de chaque classe evident a la lecture.

4. Un exemple complet

@Repository
public class EtudiantRepository {

    private final List<Etudiant> etudiants = new ArrayList<>();

    public List<Etudiant> findAll() {
        return etudiants;
    }

    public void save(Etudiant e) {
        etudiants.add(e);
    }
}
@Service
public class EtudiantService {

    private final EtudiantRepository repository;

    public EtudiantService(EtudiantRepository repository) {
        this.repository = repository;
    }

    public List<Etudiant> listerAdmis() {
        return repository.findAll().stream()
                         .filter(e -> e.getMoyenne() >= 10)
                         .toList();
    }
}
@RestController
@RequestMapping("/etudiants")
public class EtudiantController {

    private final EtudiantService service;

    public EtudiantController(EtudiantService service) {
        this.service = service;
    }

    @GetMapping("/admis")
    public List<Etudiant> admis() {
        return service.listerAdmis();
    }
}

Chaque couche ne connait que la suivante. Spring assemble le tout au demarrage.

5. Les trois formes d'injection

FormeAvis
ConstructeurRecommandee. Permet le final, facilite les tests, rend les dependances visibles
SetterAcceptable pour une dependance facultative
Champ (@Autowired)A eviter : masque les dependances et complique les tests

Depuis Spring 4.3, @Autowired est inutile sur un constructeur unique : Spring le detecte seul.

6. Les portees

PorteeSignification
singletonUne seule instance pour toute l'application : par defaut
prototypeUne nouvelle instance a chaque demande
requestUne instance par requete HTTP

Consequence a retenir : un bean singleton est partage. N'y stockez pas d'etat lie a un utilisateur, sous peine de melanger les donnees entre visiteurs.

7. Les erreurs frequentes

  • NoSuchBeanDefinitionException : la classe n'a pas d'annotation, ou se trouve hors du package scanne
  • Deux implementations d'une meme interface : Spring ne sait laquelle choisir. Utilisez @Primary ou @Qualifier
  • Dependance circulaire : A a besoin de B qui a besoin de A. C'est le signe d'un decoupage a revoir

A retenir

  • Vous ne creez pas vos objets, vous les demandez
  • Un bean est un objet gere par Spring
  • Preferez toujours l'injection par constructeur
  • Un singleton est partage : pas d'etat utilisateur dedans

Exercices d'application

Exercice 1. Trois couchesCreez CoursRepository, CoursService et CoursController avec les bonnes annotations. Faites remonter une liste de cours jusqu'a une route REST.
Exercice 2. Provoquer l'erreurRetirez l'annotation @Service de votre service et relancez. Lisez le message d'erreur et expliquez-le.
Exercice 3. Deux implementationsCreez une interface Notification avec deux implementations (mail et SMS). Faites choisir Spring avec @Primary, puis avec @Qualifier.

Cherchez avant de regarder la solution : c'est en butant sur les difficultes que l'on progresse.

Questions / Réponses

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
2 votes. Moyenne 5 sur 5.

Ajouter un commentaire

Anti-spam