"Salut Amigo."

"Bonjour Rishi."

"Aujourd'hui, je vais vous expliquer un nouveau sujet très intéressant : les proxys dynamiques" .

"Java a plusieurs façons de modifier la fonctionnalité d'une classe particulière…"

"La première méthode est l'héritage."

"Le moyen le plus simple de modifier le comportement d'une classe est de créer une nouvelle classe qui hérite de la classe d'origine (de base) et de remplacer ses méthodes. Ensuite, au lieu d'utiliser la classe d'origine, vous utilisez la classe dérivée. Par exemple :"

Reader reader = new UserCustomReader();

"La deuxième méthode consiste à utiliser une classe wrapper."

" BufferedReader est un exemple de ce type de classe. Premièrement, il hérite de Reader . En d'autres termes, il peut être utilisé à la place de Reader. Deuxièmement, il redirige tous les appels vers l' objet Reader d'origine , qui doit être transmis au constructeur de l'objet BufferedReader. . Par exemple:"

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

"La troisième méthode consiste à créer un proxy dynamique (Proxy)."

"Il existe une classe spéciale en Java (java.lang.reflect.Proxy) qui vous permet en fait de construire un objet pendant l'exécution du programme (de manière dynamique), sans créer de classe distincte pour lui."

"C'est très facile à faire :"

Reader reader = (Reader)Proxy.newProxyInstance();

"C'est déjà quelque chose de nouveau pour moi !"

"Mais bien sûr, nous n'avons pas besoin d'un objet sans méthodes. Nous avons besoin que l'objet ait des méthodes, et nous en avons besoin pour faire ce que nous voulons. Java utilise une interface spéciale pour cela appelée InvocationHandler , qui peut intercepter tous les appels de méthode associé à l'objet proxy. Un objet proxy ne peut être créé qu'à l'aide d'interfaces."

" Invoke - est le nom standard d'une méthode ou d'une classe dont la tâche principale est simplement d'appeler une méthode."

" Handler - est le nom standard d'une classe qui gère un événement. Par exemple, une classe qui gère les clics de souris s'appellera MouseClickHandler, etc."

"L'interface InvocationHandler a une seule méthode d'appel, vers laquelle tous les appels à l'objet proxy sont dirigés . Par exemple :"

Code
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

"Lors de l'appel de la méthode reader . close (), la méthode invoke sera appelée et l'écran affichera 'yes!'"

"Nous avons donc déclaré une classe CustomInvocationHandler et implémenté l' interface InvocationHandler et sa méthode d'invocation . Lorsque la méthode d'invocation est appelée, elle affiche 'oui !'. Ensuite, nous avons créé un objet CustomInvocationHandler et l'avons transmis à la méthode newProxyInstance lors de la création un objet proxy."

"Oui, c'est tout à fait exact."

"C'est un outil très puissant. Habituellement, ces proxys sont créés pour simuler des objets dans des programmes qui s'exécutent physiquement sur un autre ordinateur.  Ou pour contrôler l'accès."

"Vous pouvez vérifier les autorisations de l'utilisateur actuel, gérer les erreurs, consigner les erreurs et bien plus encore dans cette méthode."

"Voici un exemple où la méthode invoke appelle également les méthodes de l'objet d'origine :"

Code
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

"Cet exemple a deux caractéristiques spéciales."

"Tout d'abord, l' objet Reader "d'origine" est passé au constructeur et une référence à celui-ci est enregistrée dans le CustomInvocationHandler ."

"Deuxièmement, nous appelons à nouveau cette même méthode dans la méthode invoke, mais sur l'objet "original" cette fois."

"Ah. En d'autres termes, cette dernière ligne appelle la même méthode, mais sur l'objet d'origine :"

return method.invoke(readerOriginal, args);

"Ouais."

"Je ne dirais pas que c'est super évident, mais c'est toujours compréhensible. Du moins, il semble."

"Génial. Encore une chose. Dans la méthode newProxyInstance, vous devez transmettre un peu plus d'informations de gestion interne pour créer un objet proxy. Mais puisque nous ne créons pas d'objets proxy monstrueux, ces informations sont facilement obtenues à partir de la classe d'origine elle-même. "

"Voici un autre exemple :"

Code
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

"Ah. ClassLoader et liste d'interfaces. C'est quelque chose de Reflection, n'est-ce pas ?"

"Ouais."

"Je vois. Eh bien, je pense que je peux créer un objet proxy primitif et super simple si jamais j'en ai besoin."

"Alors va voir Diego."