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 :
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.
Cas négatif
Un développeur a tenté d'implémenter un audit-logging via Proxy sur des classes sans interface.
Avantages :
Inconvénients :
Cas positif
Une interface a été définie, et un proxy a été réalisé en conséquence.
Avantages :
Inconvénients :