La bibliothèque standard Java a une classe java.lang.reflect.Proxy
. .. Cette classe a été ajoutée à partir de J2SE 1.3, et la fonction fournie est généralement appelée DynamicProxy (ci-après dénommée «** Dynamic Proxy **» en japonais). Depuis la sortie de J2SE 1.3 en mai 2000, le proxy dynamique est sorti depuis longtemps et est déjà une technologie morte, mais il est toujours utilisé par de nombreux frameworks et bibliothèques Java. En revanche, il semble que les programmeurs Java en général ne soient pas très connus.
Dans cet article, pour ceux qui comprennent la syntaxe de base de Java, nous présenterons des modèles de proxy généraux pour comprendre le proxy dynamique, expliquerons la nécessité d'un proxy dynamique, puis l'expliquerons. Explique comment utiliser le proxy dynamique de Java. Il montre également comment implémenter un proxy dynamique pour des langages autres que Java (C #, Python, EcmaScript).
Le code Java publié dans cet article a été confirmé pour fonctionner dans l'environnement suivant. (L'environnement d'exploitation des langages autres que Java sera décrit à tout moment.) De plus, certaines clauses d'importation requises pour la compilation sont omises dans le code Java publié.
Nom | version |
---|---|
OpenJDK | 11.0.1 |
Comprendre comment utiliser le proxy dynamique java.lang.reflect.Proxy
ne devrait pas être difficile pour les programmeurs qui connaissent Java. Cependant, afin de comprendre la nécessité d'un proxy dynamique et de l'utiliser efficacement, comprenez le proxy « statique </ b>», qui est l'opposé de « dynamique </ b>». besoin de le faire. Cela a été expliqué dans "Je vais expliquer le modèle de proxy aussi facilement que possible".
Dans "J'expliquerai le modèle de proxy aussi facilement que possible", ** Un sur plusieurs classes qui incarnent une interface commune. Introduit jusqu'à ** points où le traitement commun peut être défini avec le proxy de. Dans cet article, nous allons commencer par cet anti-pattern.
L'application d'un proxy statique a un anti-modèle.
Voici un exemple plus spécifique du "proxy qui ajoute un traitement commun pour plusieurs classes concrètes à une interface" montré ci-dessus.
Une implémentation de la couche modèle d'un site de vente définit trois classes: "** client ", " commande " et " produit **". Pour que cette classe accède au SGBDR dans le traitement de toutes les méthodes, il est nécessaire d'obtenir une connexion SGBDR et de démarrer une transaction avant d'exécuter la méthode. Ensuite, après l'exécution, il est nécessaire de valider ou de restaurer la transaction et de fermer la connexion.
Les trois classes sont «** add » pour créer des données, « update » pour mettre à jour les données et « search» pour rechercher des données. Il semblait que ce serait suffisant s'il y avait une méthode «» et « obtenir des détails **» pour obtenir des données détaillées. Par conséquent, ces trois classes sont des classes concrètes de l'interface appelée «Model». Ensuite, définissez une classe appelée
TransactionBarrier
, qui est un proxy pour le traitement SGBDR, avec une classe concrète deModel
.
Il s'agit d'un diagramme de classes montrant la relation entre ces classes.
Dans une certaine mesure, la conception de la classe ci-dessus a bien fonctionné, mais un jour, j'ai rencontré des problèmes.
Les informations «client» doivent être supprimées en masse pour protéger les informations client.
Par conséquent, une méthode appelée "** Bulk Delete *" est ajoutée à "Clients", mais cette méthode "Batch Delete" nécessite également un pré-post-traitement du SGBDR par
TransactionBarrier
. Par conséquent, ajoutez également une méthode appelée " Supprimer tout *" à l'interfaceModèle
.Ensuite, vous devrez ajouter le "* Bulk Delete *" ajouté à l'interface
Model
aux" Commandes "et aux" Produits "qui ne nécessitent pas la méthode" * Bulk Delete * ". Cependant, en réalité, nous ne voulons pas que «Order» et «Product» appellent «Delete all», alors ajoutez la balise «@ deprecated» à la méthode «Delete all» pour «Order» et «Product». J'ai dû lancer sans condition ʻUnsupportedOperationException` lorsque cette méthode a été appelée.
Dans l'exemple ci-dessus, il est difficile de rendre compte de la gravité de la situation car une seule méthode spéciale a été ajoutée dans les trois classes, mais dans le développement réel du système, des dizaines à des centaines de modèles sont définis en fonction du projet. .. Ensuite, chaque fois que vous définissez votre propre méthode dans chaque modèle, les cas ci-dessus se produisent fréquemment, et un grand nombre de méthodes qui lancent ʻUnsuppportedOperationException` seront ajoutées à chaque classe.
La raison de tomber dans cette situation est que * un groupe de classes qui ne devrait pas être implémenté comme une interface est dérivé de force d'une interface parce que nous voulons implémenter un traitement commun avec un proxy *.
À l'origine, «** client », « commande » et « produit **» doivent avoir défini des interfaces et des proxy distincts, comme indiqué ci-dessous.
Cependant, dans la conception de classe ci-dessus, la classe proxy doit être implémentée pour chaque interface, ce qui est très gênant. En premier lieu, même s'il existe une relation un-à-un entre l'interface et la classe comme décrit ci-dessus, si vous définissez un proxy qui implémente le même traitement pour chacun, même si vous implémentez le traitement commun directement dans la classe d'implémentation, cela prendra du temps à implémenter. Même les avantages de l'utilisation d'un proxy sont perdus dans la mesure où cela ne change pas.
Ce serait bien s'il y avait un proxy qui pourrait être défini dans une implémentation pour plusieurs interfaces différentes, mais ce n'est pas possible avec la syntaxe Java normale. Cependant, avec le proxy dynamique **, vous pouvez implémenter un proxy commun sur plusieurs interfaces **.
Maintenant, introduisons un exemple d'implémentation utilisant le proxy dynamique java.lang.reflect.Proxy
(ci-après dénommé" ** Proxy
**"). </ p>
Tout d'abord, définissez la classe médiée par Proxy
et son interface. Ce code est pour MyInterface
et MyClass
publiés dans" Je vais expliquer le modèle de proxy aussi facilement que possible ". Identique à la mise en œuvre.
MyInterface.java
public interface MyInterface {
public String myMethod(String value);
}
MyClass.java
public class MyClass implements MyInterface {
@Override
public String myMethod(String value) {
System.out.println("Method:" + value);
return "Result";
}
}
Pour utiliser Proxy
, vous devez définir une classe concrète pour l'interface java.lang.reflect.InvocationHandler
. La classe concrète de ʻInvocationHandlerimplémente la méthode ʻinvoke (Object, Method, Object [])
. Cette méthode ʻinvoke` est une méthode qui implémente un traitement proxy spécifique.
ʻLe code pour MyInvocationHandler
dans l'exemple d'implémentation de la classe concrète de InvocationHandler. Dans le traitement proxy de
MyInvocationHandler,
MyInvocationHandler: startest sorti comme pré-traitement et
MyInvocationHandler: exit` est sorti comme sortie standard en post-traitement.
MyInvocationHandler.java
import java.lang.reflect.*;
/** java.lang.reflect.Classe d'implémentation InvocationHandler qui implémente le traitement proxy spécifié dans Proxy*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DynamicProxy:before");
Object result = method.invoke(target, args);
System.out.println("DynamicProxy:before");
return result;
}
}
Pour utiliser le MyInvocationHandler
défini ci-dessus, utilisez la méthode NewProxyInstance
de Proxy
pour obtenir l'objet de Proxy
.
Spécifiez l'objet de la classe MyInvocationHandler
définie ci-dessus dans le troisième argument de la méthode newProxyInstance
.
MyClass myClassObj = new MyClass();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myClassObj);
MyInterface proxiedObj = (MyInterface)Proxy.newProxyInstance(MyClass.class.getClassLoader(),
new Class[] { MyInterface.class },
myInvocationHandler);
System.out.println(proxiedObj.myMethod("Argument"));
C'est le résultat de l'exécution du code ci-dessus.
Résultat d'exécution
DynamicProxy:before
Method:Argument
DynamicProxy:after
Result
Tout d'abord, je vais expliquer la méthode newProxyInstance
de Proxy
.
Vous pouvez obtenir l'objet Proxy
avec newProxyInstance
. L'utilisateur convertit l'objet de type ʻObject acquis en l'un des types du tableau de classes de l'interface spécifiée par le deuxième argument et l'utilise. La valeur de retour de cette méthode est de type ʻObject
, mais c'est une classe dérivée de la classe Proxy
et est une classe concrète de toutes les interfaces du deuxième argument.
MyInterface proxiedObj = (MyInterface)Proxy.newProxyInstance(MyClass.class.getClassLoader(),
new Class[] { MyInterface.class },
myInvocationHandler);
newProxyInstance
. Puisque le chargeur de classe peut être obtenu à partir de toutes les classes avec la méthode getClassLoader ()
, il peut être obtenu avec le getClassLoader ()
de n'importe quelle classe, mais un environnement utilisant de nombreuses chaînes de chargeur de classe telles que les applications Web. Il est préférable de spécifier autant que possible la classe implémentée de manière unique dans l'application, en supposant qu'elle sera utilisée dans.
Ici, le chargeur de classe de MyClass, qui est une classe médiée par un proxy dynamique, est spécifié.newProxyInstance
.
En Java, une classe peut implémenter plusieurs interfaces, spécifiez donc un tableau afin de pouvoir générer un proxy dynamique qui implémente plusieurs interfaces.newProxyInstance
, spécifiez un objet de la classe concrète de ʻInvocationHandler. L'objet spécifié par cet argument est le processus qui est réellement exécuté à l'intérieur du proxy dynamique. Ici, nous avons en fait implémenté
MyInvocationHandler, qui est une classe concrète de ʻInvocationHandler
, donc cet objet est spécifié.Ensuite, je vais expliquer la méthode ʻinvoke de l'interface ʻInvocationHandler
implémentée dans MyInvocationHandler
.
La méthode ʻInvokeimplémente le traitement proxy. Chaque fois qu'une méthode qui incarne l'interface d'un objet
Proxy est appelée, cette méthode ʻinvoke
est appelée. La méthode ʻinvokeest passée comme argument quelle méthode de l'objet
Proxy` a été appelée avec quel argument.
** Si vous souhaitez implémenter un proxy général **, appelez la méthode de l'objet cible dans la méthode ʻinvoke. La méthode à appeler est passée comme argument de la méthode ʻinvoke
, mais l'objet à appeler est détenu par la classe concrète de ʻInvocationHandler` elle-même.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
est passé au ** premier argument ** de ʻinvoke
. reçoit un objet de la classe
Methodqui représente la méthode appelée par l'objet
Proxy. Dans le processus de ʻinvoke
, la valeur de cet argument est utilisée pour appeler la méthode de l'objet réel., l'argument spécifié dans la méthode dans laquelle l'objet
Proxy` est appelé est passé en tant que tableau Object.
Cela utilise également la valeur de cet argument pour appeler la méthode de l'objet réel.Si vous regardez le résultat de l'exécution de l'implémentation du proxy dynamique, vous pouvez obtenir le même résultat d'exécution que "2-2. Définition de la classe du proxy", il est donc facile de comprendre que le proxy dynamique fonctionne comme un proxy. Je vais. Mais pourquoi appeler cela "** dynamique **"?
Pour comprendre la nature «dynamique» d'un proxy dynamique, modifiez le code qui appelle le «newPorxyInstance» du «Proxy» pour qu'il s'exécute comme suit: Vous pouvez laisser MyInvocationHandler
comme l'implémentation ci-dessus.
ArrayList<String> list = new ArrayList<String>();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(list);
@SuppressWarnings("unchecked")
List<String> proxiedObj = (List<String>)Proxy.newProxyInstance(MyInterface.class.getClassLoader(),
new Class[] { List.class },
myInvocationHandler);
proxiedObj.add("hoge");
C'est le résultat de l'exécution du code ci-dessus.
Résultat d'exécution
DynamicProxy:before
DynamicProxy:after
La sortie du résultat d'exécution ci-dessus montre que l'appel à proxiedObj.add (" hoge ");
appelé ʻinvoke de
MyInvocationHandler`.
Il convient de noter ici que l'objet Proxy
de MyInterface
peut être obtenu jusqu'à présent, mais le proxy de l'interface List
peut également être obtenu sans changer du tout leMyInvocationHandler
. Le code de «MyInvocationHandler» n'a pas de caractères «MyInterface» ou «List», et «MyInvocationHandler» ne fait en fait aucune dépendance sur ces interfaces. En spécifiant la classe d'interface du deuxième argument de newProxyInstance
de Proxy
, une nouvelle classe est générée ** pendant l'exécution de newProxyInstance
** inside Proxy
, et l'objet Proxy
est acquis. Je vais.
La relation entre le proxy dynamique et la classe implémentée est indiquée dans le diagramme de classes.
La classe verte est une classe préparée avant le démarrage du processus Java, tandis que la classe rouge est créée après le démarrage du processus Java et la newProxyInstance
de Proxy
est appelée ** (instance). Non) classe **. newProxyInstance
renvoie une instance de la classe nouvellement créée (bien qu'elle soit récupérée de la carte si elle a déjà créé la même classe).
** Comment, le processus de la méthode newProxyInstance
crée une classe qui n'existe pas dans le code source! ** [^ 1]
«** Static » et « Dynamic » du proxy sont des classes de proxy « static» qui sont prédéfinies et invariantes jusqu'à la fin pendant la période d'exécution du processus Java. "", la classe proxy générée à partir du milieu du processus pendant que le processus est en cours d'exécution est appelée " dynamic **".
Ceci est la fin de l'utilisation de Proxy
.
Cependant, dans les exemples d'implémentation de proxy dynamique présentés jusqu'à présent, l'utilisateur de l'objet appelle newProxyInstance
of Proxy
pour obtenir l'objet Proxy
comprenant l'objet cible. Cependant, cette implémentation n'est pas très pratique.
Par conséquent, il existe un modèle de conception GoF appelé ** Factory Pattern **, nous allons donc utiliser ce modèle pour simplifier le code acquis par l'objet Proxy
. Le modèle de fabrique est une méthode d'acquisition d'un objet à partir d'une méthode d'une classe dédiée qui crée l'objet sans appeler le constructeur directement à l'utilisateur.
Par exemple
Exemple de modèle d'usine ①
MyInterface myObj = MyFactory.getInstance();
Si l'objet Proxy
peut être obtenu en appelant la méthode getInstance ()
de MyFactory
, l'implémentation côté utilisateur de l'objet Proxy
sera très simple.
Cependant, la méthode getInstance ()
de MyFactory
ne transmet aucune information de l'appelant, donc telle quelle, elle passe toujours par le même ʻInvocationHandler et la même classe (ici,
Proxy qui appelle l'objet de MyClass
" Vous ne pouvez obtenir que des "objets. Si vous passez la classe" MyInterface ", l'objet" MyClass "et l'objet" InvocationHandler "comme arguments, ce sera une fabrique polyvalente. Mais si vous y réfléchissez, le ʻInvocationHandler est un proxy. Puisqu'il s'agit de la définition du processus à exécuter, la fabrique peut utiliser une classe concrète fixe ʻInvocationHandler
en interne (changer la classe concrète ʻInvocationHandlerest possible en séparant la classe de fabrique et la méthode). De plus, l'objet
MyClass n'a pas besoin d'être spécifié comme argument si la classe dérivée de la classe
MyInterface est fixée sur
MyClass`.
Par conséquent, ici, nous allons implémenter une fabrique qui acquiert l'objet Proxy
en spécifiant uniquement l'interface dans l'argument de la fabrique comme indiqué ci-dessous.
Exemple de modèle d'usine ②
MyInterface myObj = MyFactory.getInstance(MyInterface.class);
En supposant que vous implémentiez une usine comme celle ci-dessus, il y a un problème.
Puisque seule la classe d'interface est spécifiée dans cet argument, il est nécessaire d'obtenir la classe dérivée pour l'interface à l'intérieur de la fabrique et de créer un objet de cette classe dérivée. La manière la plus simple de faire cela est d'avoir une carte des classes pour les interfaces dans l'usine. Cette méthode est assez fine et pratique, mais dans un système où il existe des dizaines ou des centaines de définitions d'interface et de classe, chaque fois qu'une nouvelle interface et une nouvelle classe sont définies, elles sont en usine. Vous devez ajouter une carte, ce qui est un peu gênant et peut provoquer des conflits de source dans le développement de groupe.
Par conséquent, nous allons présenter ici comment spécifier une classe dérivée dans les informations supplémentaires de l'interface. Tout d'abord, définissez une annotation comme celle-ci:
MyAnnotation.java
/**Une annotation qui spécifie la classe d'implémentation de l'interface*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
Class<?> concretizedClass();
}
Ajoutez cette annotation à «MyInterface» que vous avez définie précédemment. Dans l'argument de l'annotation, spécifiez la classe dérivée à sélectionner en usine.
MyInterface.java
/**Ajout d'une définition de classe d'implémentation pour instancier avec annotation*/
@MyAnnotation(concretizedClass = MyClass.class)
public interface MyInterface {
public String myMethod(String value);
}
Enfin, c'est l'implémentation de MyFactory
qui est une classe d'usine. La classe concrète de ʻInvocationHandler est définie par la classe anonyme dans la méthode
getInstancede
MyFactory`.
MyFactory.java
/**Une classe qui crée un objet pour l'interface spécifiée par l'argument*/
public class MyFactory {
/**Une méthode pour obtenir un objet de la classe d'implémentation de l'interface spécifiée par l'argument interfaceClass*/
public static <I> I getInstance(Class<I> interfaceClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//Obtenez la classe d'implémentation pour MyInterface à partir de l'annotation MyInterface
MyAnnotation myAnnotation = interfaceClass.getAnnotation(MyAnnotation.class);
Class<?> objClass = myAnnotation.concretizedClass();
//Créer un objet de classe d'implémentation
@SuppressWarnings("unchecked")
I obj = (I)objClass.getDeclaredConstructor().newInstance();
//Créer un objet en définissant InvocationHandler avec une classe anonyme
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DynamicProxy:before");
Object result = method.invoke(obj, args);
System.out.println("DynamicProxy:after");
return result;
}
};
//Créer un objet proxy
@SuppressWarnings("unchecked")
I proxiedObj = (I)Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class[] { interfaceClass },
invocationHandler);
return proxiedObj;
}
}
C'est le résultat de l'implémentation et de l'exécution du côté utilisateur MyFactory
ci-dessus.
MyInterface myObj = MyFactory.getInstance(MyInterface.class);
System.out.println(myObj.myMethod("Argument"));
Résultat d'exécution
DynamicProxy:before
Method:Argument
DynamicProxy:after
Result
Avec les améliorations ci-dessus, nous sommes plus proches du code "utilisable" réel, mais il y a encore de la place pour des améliorations et des problèmes qui doivent être résolus.
Ils sont répertoriés ci-dessous pour votre référence lors de l'application réelle du proxy dynamique.
getInstance
de MyFactory
lève l'exception NoSuchMethodException
qui peut se produire en interne. Les exceptions qui ne peuvent pas se produire ne doivent pas être incluses dans la clause throws spécifiée dans l'argument, vérifiez que la ʻobj Class
spécifiée dans l'annotation est une classe concrète de la ʻinterface Class`. pour ʻinterface Class
non seulement dans l'annotation d'interface mais aussi directement dans MyFactory ou à partir du fichier de configuration.telle quelle, ou de lancer l'objet
Throwable qui l'a provoquée dans
getCause ()`.getInstance ()
crée un nouvel objet Proxy
à chaque fois, mais si c'est un bon objet avec Sigletone, ne le créez pas à chaque foisProxy
d'acquérir des objets par DI (Devedency Injection) au lieu d'utiliser directement MyFactory
Le modèle qui détermine la classe dérivée pour une telle interface et renvoie un objet approprié est appelé le ** modèle ServiceLocator **, séparément du modèle de fabrique.
Jusqu'à présent, nous avons introduit le proxy dynamique par Java. Ce chapitre présente comment le proxy dynamique est fourni dans d'autres langages de programmation.
5-1.C#
(Selon cfm-art, il est dit que le standard RealProxy fournit la fonction de proxy dynamique. Ce chapitre sera à nouveau ajouté.)
~~ Il n'y a pas de classe équivalente pour le proxy dynamique dans .NET Framework, qui est une bibliothèque standard de C #. ~~ ~~ Cependant, ~~ Le Château du projet externe fournit la fonction de proxy dynamique dans la bibliothèque externe.
Nom | version |
---|---|
.NET Framework | 4.6.1 |
Castle.DynamicProxy2 | 2.2.0 |
DynamicProxyTest.cs
using Castle.Core.Interceptor;
using Castle.DynamicProxy;
namespace MyNamespace
{
public interface IMyInterface
{
string MyMethod(int value);
}
public class MyClass : IMyInterface
{
public string MyMethod(int value)
{
Console.WriteLine("MyMethod:" + value);
return "Result";
}
}
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("MyInterceptor:before");
object ret = invocation.Method.Invoke(invocation.InvocationTarget, invocation.Arguments);
Console.WriteLine("MyInterceptor:after");
invocation.ReturnValue = ret;
}
}
}
ProxyGenerator generator = new ProxyGenerator();
IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface(
typeof(IMyInterface),
new MyClass(),
new MyInterceptor()) as IMyInterface;
Console.WriteLine(proxy.MyMethod(1));
Résultat d'exécution
MyInterceptor:before
MyMethod:1
MyInterceptor:after
Result
Comme vous pouvez le voir en regardant les méthodes définies dans ProxyGenerator
, il existe de nombreuses fonctions fournies par Proxy
de Java. En Java, la classe Proxy
ne peut être créée qu'avec une classe concrète de l'interface, mais avec le C # ProxyGenerator
, vous pouvez créer un proxy dynamique pour la classe dérivée de la classe en plus de l'interface.
5-2.Python Python n'a pas non plus d'équivalent au proxy dynamique. Cependant, des méthodes spéciales standard peuvent être utilisées pour obtenir une fonctionnalité équivalente à un proxy dynamique.
Nom | version |
---|---|
Python | 3.7.1 |
dynamicProxyTest.py
class MyProxy(object):
def __init__(self, obj):
self._obj = obj
def __getattr__(self, name):
def func(*args):
print(before)
result = getattr(self._obj, name)(*args)
print('after')
return result
return func
class MyClass(object):
def myFunction(self, arg):
print('myFunction ' + arg)
return 'result'
myObj = MyClass()
myProxy = MyProxy(myObj)
print(myProxy.myFunction('arg'))
Résultat d'exécution
before
myFunction arg
after
result
Cette classe MyProxy
est une brève explication pour ceux qui ne sont pas familiers avec la syntaxe Python.
En Python, il existe une méthode spéciale (méthode spéciale) d'une méthode avec un nom spécifique avec "\ _ \ _" avant et après la définition de classe. Les méthodes spéciales sont appelées par certains événements qui se produisent sur l'objet, tels que la création d'un objet, l'appel d'un attribut ou la spécification par la valeur d'un opérateur particulier.
La méthode __getattr__
est appelée lorsque vous essayez d'obtenir un attribut qui n'est pas défini dans un objet de cette classe. L'utilisateur de l'objet obtient la valeur de retour de «getattr» comme valeur d'attribut si l'attribut avec le nom spécifié dans la classe n'est pas défini.
En Python, toutes les variables et méthodes d'une classe sont des attributs. La classe MyProxy
définit seulement deux méthodes spéciales, la méthode __init __
et la méthode __ getattr__
, elle essaie donc d'obtenir des attributs d'un objet de cette classe (c'est-à-dire qu'elle essaie d'appeler une méthode). Et la méthode __getattr__
est toujours appelée. Par conséquent, pour que MyProxy
se comporte de la même manière qu'un proxy dynamique, la méthode __getattr__
définit en interne une fonction appelée func
qui agit comme un proxy et renvoie cette fonction.
L'implémentation de l'appelant est une ligne,
myProxy.myFunction('hoge')
Cependant, cela est différent de l'appel de méthode Java.
('hoge')
.Doit être interprété comme. J'ai essayé d'obtenir l'attribut nommé «myFunction» à partir de l'objet «myProxy», mais comme «myProxy» n'avait pas l'attribut nommé «myFunction», j'obtiens la valeur de retour par la méthode «getattr» et je l'exécute.
5-3.EcmaScript(JavaScript)
EcmaScript fournit une classe appelée Proxy
à partir de 6. Cependant, il est utilisé différemment du Proxy
de Java et agit comme un gestionnaire d'événements pour l'objet.
Ici, nous allons introduire une fonction appelée ʻapply, qui est l'un des gestionnaires d'événements dans
Proxy. La fonction ʻapply
est un gestionnaire d'événements qui est appelé lorsque la fonction est appelée.
Nom | version |
---|---|
node.js | v6.11.3 |
dynamicProxyTest.js
function myFunction(arg1, arg2) {
console.log("myFunction:" + arg1 + ", " + arg2);
return "result";
}
const handler = {
apply : function (target, thisArg, argumentsList) {
console.log("before");
var result = target(...argumentsList);
console.log("after");
return result;
}
}
let proxy = new Proxy(myFunction, handler);
console.log(proxy("hoge", "hage"));
Résultat d'exécution
before
myFunction:hoge, hage
after
result
Si vous lisez jusqu'ici, vous devriez pouvoir l'implémenter avec un proxy dynamique. Cependant, en raison de son degré élevé de liberté et de ses caractéristiques spéciales, le proxy dynamique est également une technologie qui, si elle est mal utilisée, peut contenir des problèmes non fonctionnels même si elle répond aux exigences fonctionnelles. Les langages introduits dans «5. Dynamic Proxy in Other Languages» sont des langages de programmation majeurs qui sont largement utilisés au moment de la rédaction de cet article (décembre 2018), mais ce sont tous des bibliothèques standard. Il est à noter qu'il ne fournit pas de proxy dynamique. Pour le dire autrement, l'utilisation du proxy dynamique lui-même est un anti-pattern. [^ 2]
Ici, les fonctionnalités du proxy dynamique sont résumées ci-dessous.
Parmi ces caractéristiques, «2. Séparer complètement le traitement du proxy dynamique du traitement médiatisé par le proxy» est à la fois un avantage et un inconvénient du proxy dynamique. Les développeurs de classes qui implémentent des objets à médiation par proxy dynamique peuvent ne pas être conscients du fait que leurs classes sont appelées par médiation de proxy dynamique dans l'environnement dans lequel l'application s'exécute réellement. Cependant, en supposant que la médiation de proxy dynamique est essentielle pour que la classe fonctionne, à l'intérieur du proxy pour réutiliser la classe que vous avez créée dans un autre environnement ou pour l'exécuter dans votre propre environnement pour les tests. La difficulté augmentera car vous devez reproduire le processus de. Cela peut également rendre difficile la résolution des erreurs et des échecs.
L'alternative la plus simple au proxy dynamique consiste à utiliser une expression lambda pour définir le proxy comme suit:
MyInterface myObj = new MyClass();
MyProxy.invoke(() -> { nyObj.myMethod(); });
Dans la méthode MyProxy
ʻinvoke, vous pouvez implémenter le traitement proxy et appeler l'objet
Runnablepassé comme argument pour appeler le traitement d'origine qui a été médié. Cette méthode vous permet également d'implémenter un proxy entre les interfaces et les classes. De plus, les développeurs peuvent voir quel type de médiation la classe qu'ils définissent est appelée en regardant l'implémentation
MyProxy`.
Sur la base de ce qui précède, je vais donner un exemple d'utilisation qui semble être un anti-pattern de la méthode d'application du proxy dynamique. Cependant, ces modèles ne sont pas nécessairement des modèles qui ne devraient pas être utilisés. Est-il vraiment préférable d'appliquer un proxy dynamique? Existe-t-il d'autres alternatives plus efficaces? C'est un anti-pattern dans le sens où il vaut mieux considérer. La signification originale d'un proxy est un agent. Par conséquent, j'introduirai l'anti-motif ci-dessous sous le titre «agent de ○○».
Par exemple, dans le processus de vérification de la valeur d'entrée dans la méthode de récupération des informations dans la classe définie pour gérer des informations limitées telles que les utilisateurs dans un énorme système Web, il est dynamique. L'application d'un proxy peut ne pas être très souhaitable. Même si la normalisation et la normalisation du traitement sont nécessaires en raison du grand nombre d'éléments de valeur d'entrée, il existe d'autres moyens de normaliser certains des nombreux traitements différents autres que le proxy dynamique.
Le proxy dynamique a pour effet secondaire de rendre difficile la lecture de l'ensemble du processus à partir du code car il sépare complètement le processus. Même si l'échelle de traitement dans une petite zone devient grande, il serait souhaitable de la mettre en œuvre de manière à ce que l'ensemble puisse être saisi.
Supposons que le contenu de la classe concrète de ʻInvocationHandler utilisée par
Proxy` ne soit pas exposé, mais que le développeur soit obligé d'implémenter une classe qui ne peut pas être exécutée sans passer par le traitement du proxy dynamique. Il peut être stressant pour les développeurs de garder secrets les prérequis pour les implémentations qu'ils développent. Bien sûr, si le système rencontre des problèmes causés par le traitement de proxy dynamique, ainsi que des problèmes psychologiques, les développeurs peuvent passer beaucoup de temps à les étudier. En outre, il peut être difficile de réutiliser le traitement mis en œuvre.
C'est un cas où la responsabilité du proxy dynamique devient ambiguë. Initialement, le proxy dynamique n'était implémenté que pour les opérations non système telles que la sortie de journal et le traitement d'initialisation / terminaison lié à la base de données, mais le traitement d'analyse du corps de la requête HTTP est inclus et un traitement de pré-vérification est effectué. Il s'agit d'un cas où la responsabilité du proxy dynamique augmente à mesure que le processus d'approbation est engagé.
Il ne s'agit pas seulement de procurations dynamiques, mais les responsabilités de classe doivent être clarifiées. Vous devez également suivre cette politique lorsque vous effectuez des corrections. Si nécessaire, vous devez passer par plusieurs proxy dynamiques avec des responsabilités différentes.
Dans le cas où le système a une structure multicouche telle que la couche contrôleur, la couche service, la couche logique et la couche d'accès aux données, ou dans un système dans lequel les objets dépendent les uns des autres de manière compliquée, tous les objets agissent comme des agents. Dans cette situation, vous pouvez avoir besoin d'un proxy pour le traitement, mais vous pouvez imaginer qu'il existe de nombreux proxy inutiles. Vous ne devez négocier que ce dont vous avez besoin et déterminer attentivement si vous avez vraiment besoin d'autres médiations par procuration.
Comme beaucoup d'entre vous l'ont peut-être remarqué en lisant l'exemple d'implémentation de la classe concrète de ʻInvocationHandler,
Proxy n'applique pas l'implémentation du modèle de proxy. Dans la méthode ʻinvoke
de ʻInvocationHandler, il appartient entièrement à l'implémenteur de la classe concrète ʻInvocationHandler
d'appeler réellement la méthode passée en argument. Par conséquent, «Proxy» peut être appliqué à des modèles autres que proxy.
Par exemple, DTO (Data Transfer Object) est un modèle qui définit les membres de classe et leurs méthodes set et get correspondantes. Ceci est dû au fait que la définition DTO n'est que l'interface qui déclare la méthode set et la méthode get, et l'implémentation interne est définie par ʻInvocation Hadler`.
MyDtoInvocationHadler.java
public class MyDtoInvocationHandler implements InvocationHandler {
private Map<String, Object> map = new HashMap<String, Object>();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Traitement des méthodes commençant par get
if (method.getName().startsWith("get")) {
return map.get(method.getName().substring(3));
}
//Traitement des méthodes commençant par set
else if (method.getName().startsWith("set")) {
//réduction
}
}
}
Il est peu probable que cette application soit vraiment utile. Même si vous pouvez omettre l'implémentation de la classe DTO, vous devez tout de même définir l'interface et vous devez définir la méthode set et la méthode get pour chaque élément de cette interface. Dans la plupart des projets de développement, l'implémentation de la classe Entity correspondant à la classe DTO et à la table DB aura un outil qui sort automatiquement le code du document de conception, etc. au lieu de l'écriture manuscrite. Dans ce cas, peu importe que le code de sortie soit une classe ou une interface. Inversement, cela prive les développeurs de la possibilité d'incorporer des implémentations autres que de simples interactions de membres de classe dans un DTO particulier.
Ce n'est pas un restaurant. Agent qui nécessite une grande quantité d'informations dans les annotations d'interface et les fichiers de configuration pour assurer la médiation de l'agent. Cela a aussi tendance à se produire lorsque vous essayez de faire effectuer à Proxy
un traitement autre que proxy, comme dans" 6-5. Comme un agent ". Si vous passez une commande pour une grande quantité d'informations auprès d'un utilisateur d'agent, ne devrait-elle pas être mise en œuvre par l'utilisateur au lieu de l'agent?
C'est tout pour l'explication du proxy dynamique dans cet article. Pour des explications plus détaillées, veuillez lire le site de référence ci-dessous.
Comprendre le proxy dynamique n'est pas du tout difficile, mais comme je l'ai mentionné dans "1. Introduction", d'après mon expérience personnelle, je pense que les programmeurs Java généraux de proxy dynamique ne sont pas très connus. Je vais. Nous pensons que cela est dû aux raisons suivantes.
Cependant, si vous êtes un programmeur qui a travaillé sur certains projets de développement Java, lorsque vous exécutez votre code sur le framework, la trace de la pile montre un appel de classe inconnu. Vous êtes-vous déjà senti étrange en voyant? Pour répondre à ces questions, il est judicieux de comprendre le proxy dynamique. De plus, je ne pense pas que la connaissance du proxy dynamique de Java soit gaspillée lors de l'apprentissage d'autres langages de programmation à partir de Java.
D'un autre côté, alors que la sortie de J2SE 1.3 incluant Proxy
était en mai 2000, il est hors de propos que Martin Fowler ait proposé POJO (Plane Old Java Object) en septembre 2000. Cependant, je n'ai pas pu le gérer dans cet article en raison d'un manque de préparation.
Nous espérons que cet article vous donnera l'occasion d'acquérir des connaissances sur le proxy dynamique.
https://docs.oracle.com/javase/jp/10/docs/api/java/lang/reflect/Proxy.html https://docs.oracle.com/javase/jp/8/docs/technotes/guides/reflection/proxy.html https://www.ibm.com/developerworks/jp/java/library/j-jtp08305/
[^ 1]: Strictement parlant, la documentation Java officielle affichée sur le site de référence ci-dessus dit seulement «parce que le code de la classe proxy est généré par un code système de confiance», dans la méthode «newProxyInstnce». Ne spécifie pas qu'il générera une nouvelle classe. Cela dépend vraiment de l'implémentation de la VM, et il est possible que vous implémentiez un proxy dynamique avec une approche de type Python. Cependant, la classe Proxy
ne sait pas quel type de classe proxy dynamique est requis tant que la classe d'interface n'est pas spécifiée dans l'argument de la méthode newProxyInstnce
, elle est donc interprétée comme créant une nouvelle classe dans la méthode newProxyInstnce
. Je suis.
[^ 2]: C'est un argument extrême, mais l'utilisation du proxy dynamique doit toujours être considérée avec suffisamment de soin pour soupçonner qu'il peut tomber dans un anti-pattern.