Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 57

Introduction aux problèmes résolus par Spring

Recommended resources

Bonjour ! Félicitations pour avoir mené à bien le projet d'application CRUD complète. Vous avez assemblé toutes les pièces du puzzle Java Web "classique" : JDBC, DAO, Servlets et JSP. En réalisant ce projet, vous avez sans doute remarqué la quantité de code de "plomberie" nécessaire pour initialiser le DAO, le rendre disponible aux servlets, et connecter les différentes couches.

Cette expérience est le point de départ idéal pour notre nouveau module. Nous allons maintenant découvrir comment les frameworks modernes, et en particulier Spring, ont été conçus pour éliminer cette complexité. Cette première leçon se concentre sur le "pourquoi" : pourquoi avons-nous besoin d'un framework comme Spring ?

À la fin de cette leçon, vous serez capable d'expliquer les deux problèmes fondamentaux que Spring résout : le couplage fort et la gestion manuelle des dépendances. Comprendre ces problèmes est la clé pour apprécier la puissance et l'élégance de Spring.

1. Le symptôme : des applications rigides et difficiles à maintenir

Quand une application grandit, ajouter de nouvelles fonctionnalités ou simplement changer un détail technique peut devenir un véritable casse-tête si l'architecture n'est pas flexible. La vidéo suivante illustre parfaitement ce problème à travers l'exemple d'une application de facturation qui doit s'adapter à de nouveaux clients.

Spring et Java : Pourquoi a t-on besoin de Spring ?

Cette vidéo de Jean-Philippe Ehret raconte l'histoire d'une application qui évolue. Elle met en lumière les difficultés rencontrées lorsqu'on n'utilise pas de framework pour gérer la complexité.

Regardez la première partie de la vidéo, intitulée Un code rigide. Portez une attention particulière à la manière dont l'ajout de nouvelles exigences client (un nouveau système de numérotation, une nouvelle façon de saisir les données) oblige les développeurs à modifier le code existant et à ajouter des structures conditionnelles (if-else) de plus en plus complexes.

Comme vous l'avez vu, l'application devient rigide. Chaque changement a des répercussions dans plusieurs parties du code. La cause principale de cette rigidité est un concept que nous appelons le couplage fort.


2. Le problème de fond : le couplage fort

Le couplage fort se produit lorsqu'une classe dépend directement d'une implémentation concrète d'une autre classe. Dans notre projet CRUD, même en utilisant une DAOFactory, la servlet dépendait implicitement de la classe ProduitDAOImpl. Le maillon faible est souvent l'opérateur new.

Quand vous écrivez ClasseA objet = new ClasseB();, vous créez un lien direct et rigide entre ClasseA et ClasseB. Si demain vous voulez que ClasseA utilise ClasseC à la place de ClasseB, vous êtes obligé de modifier le code source de ClasseA.

L'exemple suivant, tiré de la ressource LINK, illustre ce problème de manière très simple.

JEE Servlet,jsp,beans,spring MVC,Spring Boot - FixWins.com

Cet extrait de documentation vous montre un exemple de code minimaliste qui met en évidence le problème du couplage fort.

Lisez la section "Exemple sans IoC" et la ligne juste en dessous qui commence par "Problème :". Remarquez comment la classe UserService instancie elle-même sa dépendance UserRepository avec this.repository = new UserRepository();. C'est l'essence même du couplage fort. UserService est maintenant marié à cette implémentation spécifique de UserRepository.

Ce couplage pose plusieurs problèmes :

  • Maintenance difficile : Changer l'implémentation UserRepository (par exemple, pour se connecter à une autre base de données) force la modification de UserService.
  • Testabilité réduite : Il est très difficile de tester UserService de manière isolée. Pour les tests, on aimerait lui fournir une fausse implémentation (MockUserRepository) qui ne se connecte pas à une vraie base de données. Avec le couplage fort, c'est impossible sans modifier le code.

3. Le principe de la solution : le couplage faible grâce aux interfaces

La solution à ce problème est de faire en sorte que nos classes ne dépendent plus d'implémentations concrètes, mais d'abstractions, c'est-à-dire d'interfaces.

Le principe est le suivant : UserService ne doit pas connaître DatabaseUserRepository. Il doit simplement savoir qu'il a besoin de "quelque chose" qui respecte le contrat défini par l'interface UserRepository.

La vidéo suivante, bien que plus longue et technique, est l'une des meilleures explications du passage d'un couplage fort à un couplage faible. Elle va vous donner une compréhension profonde de ce concept fondamental en génie logiciel.

Part 1 Mise en oeuvre de l'injection des dépendances

Le professeur Mohamed Youssfi décortique ici les notions de couplage fort et faible. C'est le cœur théorique de notre leçon.

Concentrez-vous sur ces deux passages cruciaux : Le couplage fort : Cette partie explique en détail, avec des schémas, pourquoi une dépendance directe (symbolisée par l'opérateur new) devient un "cauchemar de maintenance". La solution : le couplage faible : Ici, l'interface est introduite comme la solution. L'idée clé est résumée par le principe de conception : "Dépendez des abstractions (interfaces), pas des classes concrètes.". Les analogies avec les ports USB et les procédures administratives sont particulièrement éclairantes pour bien saisir l'idée.

Grâce aux interfaces, notre code devient "fermé à la modification mais ouvert à l'extension". On peut ajouter de nouvelles implémentations sans jamais toucher aux classes qui les utilisent.


4. L'automatisation de la solution : l'Inversion de Contrôle (IoC)

Nous savons maintenant comment concevoir notre application pour qu'elle soit flexible (en utilisant des interfaces). Mais une question demeure : si UserService ne fait plus new UserRepository(), qui le fait ? Qui est responsable de créer l'objet DatabaseUserRepository et de le "donner" (l'injecter) à UserService ?

Dans le projet CRUD, nous avons répondu à cette question manuellement avec la DAOFactory et le ServletContextListener. C'est ce qu'on appelle la gestion manuelle des dépendances. C'est mieux que rien, mais c'est du code répétitif, technique, et source d'erreurs.

La solution ultime est de déléguer entièrement cette tâche à un framework. C'est le principe de l'Inversion de Contrôle (Inversion of Control - IoC).

Au lieu que notre code contrôle la création et l'assemblage de ses dépendances, on inverse le contrôle : on laisse un composant externe, appelé conteneur IoC, s'en charger.

Ce diagramme montre comment le conteneur IoC de Spring fonctionne. Il prend vos objets métier (de simples classes Java, ou POJOs) et des métadonnées de configuration, et il s'occupe de tout assembler pour produire une application fonctionnelle. Il automatise la "plomberie" que nous faisions manuellement.

Le texte suivant formalise cette notion de conteneur et la distingue d'une simple bibliothèque.

L'inversion de contrôle — Spring Framework / Spring Boot

Cet article de Gayerie.dev offre une définition claire et formelle de l'Inversion de Contrôle et du rôle du conteneur IoC.

Lisez les sections "L’inversion de contrôle" et "Notion de conteneur IoC". Votre objectif est de bien saisir : La différence fondamentale entre une bibliothèque (que votre code appelle) et un framework (qui appelle votre code). L'IoC est ce qui caractérise un framework. Le rôle du conteneur IoC : il est responsable de la création des objets et de s'assurer que leurs dépendances sont correctement satisfaites.

C'est là que Spring entre en jeu. Le framework Spring est, à son cœur, un conteneur IoC très puissant. Il a été créé précisément pour résoudre les problèmes de couplage fort et de gestion manuelle des dépendances qui rendaient les applications Java complexes difficiles à gérer.


Conclusion

Résumons les points essentiels de cette leçon :

  • Couplage fort : C'est lorsque les classes dépendent directement d'implémentations concrètes (souvent via new). Cela rend les applications rigides, difficiles à maintenir et à tester.
  • Gestion manuelle des dépendances : C'est le fait d'écrire soi-même le code "de plomberie" (comme notre DAOFactory) pour créer et connecter les objets. C'est répétitif et source d'erreurs.
  • Inversion de Contrôle (IoC) : C'est le principe de conception qui résout ces problèmes. On délègue la responsabilité de la création et de l'assemblage des composants à un conteneur (le framework).
  • Spring est avant tout un conteneur IoC. Son but premier est de nous libérer de la gestion des dépendances pour que nous puissions nous concentrer sur la logique métier.

Maintenant que vous comprenez pourquoi Spring existe et quels problèmes fondamentaux il résout, nous sommes prêts pour la prochaine étape. Dans la leçon suivante, nous allons étudier le mécanisme principal par lequel Spring met en œuvre l'IoC : l'Injection de Dépendances (DI). Vous verrez concrètement comment dire à Spring "cette classe a besoin de cette dépendance" et le laisser faire la magie.