Pour proxy une classe avec le proxy dynamique jdk, la classe proxy doit implémenter au moins une interface, et seules les méthodes de l'interface peuvent être proxy.
L'implémentation du proxy dynamique dans jdk est généralement divisée en trois étapes :
1. Écrivez des interfaces et des classes d'implémentation.
2. Écrivez un processeur qui implémente l'interface InvocationHandler. Cette interface n'a qu'une seule méthode et sa signature est public Object Invocation (Object Proxy, Method Method, Object[] args).
throws Throwable;Vous pouvez ajouter votre propre code dans la méthode d'implémentation du processeur avant et après l'appel de méthode pour effectuer une interception dynamique. Il convient de noter que la classe proxy dynamique générée par proxy n'est pas la classe réelle que nous représentons par proxy, nous pouvons donc ajouter une variable membre de type Object au processeur pour pointer vers la classe que nous voulons vraiment être proxy (c'est-à-dire la classe proxy dynamique générée par proxy). classe dans la classe de mise en œuvre de l’étape 1).
3. Utilisez la méthode newProxyInstance de la classe java.lang.reflect.Proxy pour générer une classe proxy dynamique. Pour tous les appels aux méthodes proxy, vous pouvez appeler directement la méthode de la classe proxy dynamique générée, mais vous devez d'abord effectuer une conversion de type forcée sur celle-ci et la convertir dans l'interface de la méthode que nous souhaitons appeler.
Analyse du principe du JDK :
En analysant le code source de Proxy, vous pouvez voir la génération détaillée des classes proxy dynamiques. La méthode newProxyInstance génère d’abord une instance Class de la classe proxy dynamique, puis appelle son constructeur dont le type de paramètre est InvocationHandler pour générer la classe proxy dynamique et renvoyer.
Comment générer l'instance Class de la classe proxy dynamique ? La classe ProxyGenerator est utilisée pour générer le flux d'octets de classe de la classe proxy dynamique et le charger dans la zone de méthode.
En analysant le processus de génération du flux d'octets de classe, nous pouvons voir qu'il utilise Proxy comme classe parent pour implémenter toutes les méthodes de l'interface à proxy. Le corps d'implémentation de chaque méthode appelle principalement la méthode d'invocation du processeur.
Le code principal du processus de génération de flux d'octets de classe est le suivant :
Copiez le code comme suit :
octet privé[] generateClassFile()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
pour(int i = 0; i < interfaces.length; i++)
{
Méthode amethod[] = interfaces[i].getMethods();
pour(int k = 0; k < amethod.length; k++)
addProxyMethod(améthode[k], interfaces[i]);
}
Liste de liste ;
for(Itérateur iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (Liste)iterator.next();
essayer
{
méthodes.add(generateConstructor());
for(Itérateur iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();)
{
Liste list1 = (Liste)iterator1.next();
Itérateur iterator2 = list1.iterator();
tandis que (iterator2.hasNext())
{
ProxyMethod proxymethod = (ProxyMethod)iterator2.next();
field.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
méthodes.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
Copiez le code comme suit :
}
}
méthodes.add(generateStaticInitializer());
}
catch (IOException ioexception)
{
throw new InternalError("Exception d'E/S inattendue");
}
si(methods.size() > 65535)
throw new IllegalArgumentException("limite de méthode dépassée");
si(fields.size() > 65535)
throw new IllegalArgumentException("limite de champ dépassée");
cp.getClass(dotToSlash(className));
cp.getClass("java/lang/reflect/Proxy");
pour(int j = 0; j < interfaces.length; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
essayer
{
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(dataoutputstream);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(interfaces.length);
pour(int l = 0; l < interfaces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
FieldInfo information de champ ;
Copiez le code comme suit :
//Ajouter des attributs
for(Itérateur iterator3 = field.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//Ajouter une méthode
dataoutputstream.writeShort(methods.size());
MethodInfo méthodeinfo;
for(Itérateur iterator4 = méthodes.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
}
capture (IOException ioexception1)
{
throw new InternalError("Exception d'E/S inattendue");
}
return bytearrayoutputstream.toByteArray();
}
Remarque : La partie rouge du code, proxymethod.generateMethod(), génère le corps de méthode pour chaque méthode. En regardant le code source, nous pouvons voir qu'elles appellent toutes la méthode d'invocation du processeur d'implémentation de l'interface InvocationHandler.