Um eine Klasse mit einem dynamischen JDK-Proxy zu vertreten, muss die Proxy-Klasse mindestens eine Schnittstelle implementieren und nur Methoden in der Schnittstelle können als Proxy verwendet werden.
Die Implementierung eines dynamischen Proxys in JDK ist im Allgemeinen in drei Schritte unterteilt:
1. Schreiben Sie Schnittstellen und Implementierungsklassen.
2. Schreiben Sie einen Prozessor, der die InvocationHandler-Schnittstelle implementiert. Diese Schnittstelle hat nur eine Methode und ihre Signatur ist öffentlicher Objektaufruf (Objektproxy, Methodenmethode, Objekt[]-Argumente).
throws Throwable; Sie können der Implementierungsmethode des Prozessors vor und nach dem Methodenaufruf Ihren eigenen Code hinzufügen, um ein dynamisches Abfangen durchzuführen. Es ist zu beachten, dass die vom Proxy generierte dynamische Proxy-Klasse nicht die tatsächliche Klasse ist, die wir als Proxy verwenden. Daher können wir dem Prozessor eine Mitgliedsvariable vom Typ „Object“ hinzufügen, um auf die Klasse zu verweisen, für die wir tatsächlich einen Proxy verwenden möchten (d. h. die). Klasse in Schritt 1 Implementierungsklasse).
3. Verwenden Sie die newProxyInstance-Methode der Klasse java.lang.reflect.Proxy, um eine dynamische Proxy-Klasse zu generieren. Bei allen Aufrufen von Proxy-Methoden können Sie die Methode der generierten dynamischen Proxy-Klasse direkt aufrufen. Sie müssen jedoch zunächst eine erzwungene Typkonvertierung durchführen und sie in die Schnittstelle der Methode konvertieren, die wir aufrufen möchten.
JDK-Prinzipanalyse:
Durch die Analyse des Quellcodes von Proxy können Sie die detaillierte Generierung dynamischer Proxy-Klassen sehen. Die newProxyInstance-Methode generiert zunächst eine Klasseninstanz der dynamischen Proxy-Klasse und ruft dann ihren Konstruktor auf, dessen Parametertyp InvocationHandler ist, um die dynamische Proxy-Klasse zu generieren und zurückzugeben.
Wie generiert man die Klasseninstanz der dynamischen Proxy-Klasse? Die ProxyGenerator-Klasse wird verwendet, um den Klassenbyte-Stream der dynamischen Proxy-Klasse zu generieren und in den Methodenbereich zu laden.
Wenn wir den Prozess der Klassenbyte-Stream-Generierung analysieren, können wir sehen, dass Proxy als übergeordnete Klasse verwendet wird, um alle Methoden der Schnittstelle zu implementieren, die als Proxy verwendet werden sollen. Der Implementierungskörper jeder Methode ruft hauptsächlich die Aufrufmethode des Prozessors auf.
Der Hauptcode des Klassenbyte-Stream-Generierungsprozesses lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
privates Byte[] genericClassFile()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
for(int i = 0; i < interfaces.length; i++)
{
Methode amethod[] = interfaces[i].getMethods();
for(int k = 0; k < amethod.length; k++)
addProxyMethod(amethod[k], interfaces[i]);
}
Listenliste;
for(Iterator iterator = ProxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (List)iterator.next();
versuchen
{
methoden.add(generateConstructor());
for(Iterator iterator1 = ProxyMethods.values().iterator(); iterator1.hasNext();)
{
Liste list1 = (List)iterator1.next();
Iterator iterator2 = list1.iterator();
while(iterator2.hasNext())
{
ProxyMethod Proxymethod = (ProxyMethod)iterator2.next();
Fields.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
methoden.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
Kopieren Sie den Codecode wie folgt:
}
}
methoden.add(generateStaticInitializer());
}
Catch(IOException ioException)
{
throw new InternalError("unexpected I/O Exception");
}
if(methods.size() > 65535)
throw new IllegalArgumentException("Methodenlimit überschritten");
if(fields.size() > 65535)
throw new IllegalArgumentException("Feldlimit überschritten");
cp.getClass(dotToSlash(className));
cp.getClass("java/lang/reflect/Proxy");
for(int j = 0; j < interfaces.length; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
versuchen
{
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);
for(int l = 0; l < interfaces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
FieldInfo fieldinfo;
Kopieren Sie den Codecode wie folgt:
//Attribute hinzufügen
for(Iterator iterator3 = field.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//Methode hinzufügen
dataoutputstream.writeShort(methods.size());
MethodInfo methodinfo;
for(Iterator iterator4 = methoden.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
}
Catch(IOException ioException1)
{
throw new InternalError("unexpected I/O Exception");
}
return bytearrayoutputstream.toByteArray();
}
Hinweis: Der rote Teil im Code, Proxymethod.generateMethod(), generiert den Methodenkörper für jede Methode. Wenn wir uns den Quellcode ansehen, können wir sehen, dass sie alle die Aufrufmethode des Implementierungsprozessors der InvocationHandler-Schnittstelle aufrufen.