L'architecture proxy de Spring : un écosystème de beans dédoublés
Contexte
Spring est un framework Java qui facilite le développement d'applications d'entreprise en fournissant une infrastructure complète. Au cœur de Spring se trouve le conteneur d'inversion de contrôle (IoC) qui gère le cycle de vie des objets et leurs dépendances. Pour implémenter certaines de ses fonctionnalités les plus puissantes comme l'AOP (Aspect-Oriented Programming) ou les transactions, Spring utilise un mécanisme appelé "proxy".
Quelques mots sur le proxy
Un proxy, dans le contexte de la programmation, est un objet qui agit comme un intermédiaire entre un client et un objet cible. Le proxy possède la même interface que l'objet cible, ce qui le rend transparent pour le client. Cependant, il peut intercepter les appels aux méthodes, les modifier, les enrichir ou même les bloquer avant de les transmettre à l'objet cible.
Il existe deux types principaux de proxys dans Spring :
- Les proxys JDK Dynamic : utilisés par défaut lorsque la classe cible implémente une interface
- Les proxys CGLIB : utilisés lorsque la classe cible n'implémente pas d'interface ou lorsqu'on le configure explicitement
La mécanique cachée des beans Spring
Lorsqu'on travaille avec Spring, il est important de comprendre que le framework crée effectivement deux fois plus d'objets que ce qu'on pourrait imaginer pour certains types de beans. Pour chaque bean qui nécessite des fonctionnalités transversales comme les transactions, l'AOP ou la validation, Spring crée en réalité:
- Le bean cible original : l'instance réelle de la classe que vous avez définie
- Le bean proxy : une enveloppe autour du bean original qui intercepte les appels
Quels beans sont concernés ?
Ce dédoublement concerne principalement les beans qui utilisent:
- @Transactional : tous les beans avec des méthodes transactionnelles
- AOP : les beans ciblés par des aspects (logging, sécurité, etc.)
- @Validated : les beans qui nécessitent une validation
- @Async : les beans avec des méthodes asynchrones
- @Cacheable et autres annotations de cache
Pourquoi cette architecture dédoublée ?
Cette architecture en deux couches permet à Spring d'implémenter le principe de séparation des préoccupations:
- Séparation du code métier et des services techniques : votre code métier reste propre et centré sur sa responsabilité
- Application transparente des fonctionnalités transversales : le proxy ajoute les fonctionnalités sans que le code métier n'ait à s'en préoccuper
- Respect du principe "Open/Closed" : on étend les fonctionnalités des beans sans modifier leur code source
Implications pratiques
Ce mécanisme de dédoublement a plusieurs conséquences:
- Consommation mémoire : Spring crée effectivement plus d'objets que le nombre de classes que vous définissez
- Performance d'initialisation : la création de proxies dynamiques ajoute un temps à l'initialisation du contexte
- Comportement des appels internes : les appels de méthode au sein d'un même bean contournent le proxy, ce qui peut désactiver certaines fonctionnalités (notamment les transactions)
- Débogage : lors du débogage, vous pouvez remarquer que l'instance réelle est enveloppée dans un proxy
Comprendre cette architecture dédoublée est essentiel pour diagnostiquer certains problèmes courants dans les applications Spring et pour concevoir correctement vos services, particulièrement lorsqu'il s'agit de transactions ou d'autres fonctionnalités qui dépendent de l'interception des appels de méthodes.
Cycle de création du proxy
- Initialisation du contexte Spring
- Lorsque l'application démarre, Spring scanne les classes annotées (comme
@Component
,@Service
, etc.) - Spring identifie les beans qui nécessitent un proxy (ceux ayant des annotations comme
@Transactional
ou ciblés par des aspects)
- Lorsque l'application démarre, Spring scanne les classes annotées (comme
- Création du bean et de son proxy
- Spring crée d'abord l'instance du bean réel
- Ensuite, si nécessaire, Spring génère un proxy autour de ce bean
- Le proxy est créé en utilisant soit JDK Dynamic Proxy (si le bean implémente une interface) soit CGLIB (si le bean n'implémente pas d'interface)
- Enregistrement dans le conteneur
- Le proxy, et non le bean original, est enregistré dans le conteneur Spring
- Toutes les injections de dépendances et les références pointent vers le proxy
- Les clients interagissent uniquement avec le proxy, sans savoir qu'il s'agit d'un proxy
Fréquence de création
- Une seule fois par bean : Le proxy est créé une seule fois lors de l'initialisation du contexte Spring, et non à chaque appel de méthode
- Réutilisation : Le même proxy est réutilisé pour tous les appels ultérieurs à toutes les méthodes du bean
- Singleton : Dans la portée par défaut (singleton), il n'y a qu'une seule instance du proxy par contexte Spring
À chaque appel de méthode, le proxy existant intercepte l'appel, applique la logique nécessaire (comme démarrer une transaction ou exécuter un aspect), puis délègue l'appel au bean réel.
Aucun commentaire:
Enregistrer un commentaire