ProgrammationDéveloppeur Backend

Quelle est la nature du mécanisme des proxys dynamiques en Java, comment est-il réalisé et à quoi sert-il ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Contexte :

Le modèle de Proxys Dynamiques est apparu dans Java 1.3 et a permis la réalisation de modèles AOP, de journalisation, de sécurité, de génération dynamique de code sans avoir à écrire manuellement des classes de proxy. Cela est devenu très populaire dans Spring, Hibernate et d'autres frameworks.

Problème :

Il arrive que l'on doive ajouter des fonctionnalités à une interface existante ou modifier le comportement des méthodes à l'exécution, sans changer le code source. Exemple : journaliser chaque appel de méthode, vérifier l'accès, profiler. Le principal problème est l'impossibilité de le faire manuellement pour toutes les classes, surtout s'il y a beaucoup de classes ou si elles sont générées dynamiquement.

Solution :

Java fournit une API standard dans le paquet java.lang.reflect pour créer des objets proxy "à la volée" pour toutes les interfaces à l'aide de Proxy.newProxyInstance. On implémente un objet InvocationHandler qui intercepte chaque appel de méthode et implémente sa propre logique.

Exemple de code :

import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Serving..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();

Caractéristiques clés :

  • Fonctionne uniquement avec des interfaces
  • Permet d'ajouter un comportement à toutes les méthodes de l'interface
  • Utilisé pour AOP, journalisation, contrôle des transactions

Questions pièges.

Peut-on utiliser un proxy dynamique standard pour "enrober" une classe normale qui n'implémente pas d'interfaces ?

Non. Le Proxy Dynamique ne fonctionne qu'avec des interfaces. Pour les classes, il faut manipuler le bytecode (par exemple, CGLIB).

Que se passe-t-il si une interface contient des méthodes avec un type de retour incompatible avec la valeur de retour de l'InvocationHandler ?

Une ClassCastException sera lancée, donc la valeur de retour doit être strictement compatible avec la définition de l'interface.

Peut-on utiliser un proxy dynamique pour des méthodes finales ?

Non. Les Proxys ne fonctionnent qu'au niveau des interfaces, et il n'y a pas de méthodes finales à ce niveau. Les méthodes finales ne peuvent pas être redéfinies du tout.

Erreurs communes et anti-modèles

  • Tentative d'utiliser un proxy dynamique pour des classes sans interfaces
  • Erreurs de retour de types dans les méthodes de l'InvocationHandler
  • Gestion des exceptions échouée dans invoke

Exemple concret

Cas négatif

Un développeur a tenté d'implémenter un audit-logging via Proxy sur des classes sans interface.

Avantages :

  • Tentait de le faire rapidement et "de manière transparente"

Inconvénients :

  • A rencontré des bugs en production : le proxy ne fonctionnait pas avec des classes normales ne réalisant pas d'interface
  • Perte de temps en débogage

Cas positif

Une interface a été définie, et un proxy a été réalisé en conséquence.

Avantages :

  • Flexibilité pour ajouter de nouveaux aspects (journalisation, sécurité)
  • Simplicité de test

Inconvénients :

  • Nécessité de revoir un peu l'architecture