Das Problem besteht darin, eine dynamisch erweiterte Version vorhandener Objekte zu erstellen.Wie implementiert man einen Wrapper Decorator in Java?
Ich kann die Class
des Objekts nicht ändern. Stattdessen habe ich zu:
- Unterklasse es
- wickeln das vorhandene Objekt in der neuen
Class
- delegieren alle die ursprüngliche Methode zur umschlungenen Objekt aufruft
- alle Methoden implementieren, die durch eine andere Schnittstelle definiert sind
Die Schnittstelle zu bestehenden Objekten hinzuzufügen ist:
public interface EnhancedNode {
Node getNode();
void setNode(Node node);
Set getRules();
void setRules(Set rules);
Map getGroups();
void setGroups(Map groups);
}
Mit Byte Buddy gelang es mir, Unterklasse und meine Schnittstelle zu implementieren. Das Problem ist die Delegierung an das umschlossene Objekt. Der einzige Weg, dies zu tun, den ich gefunden habe, ist die Verwendung von Reflektion, was zu langsam ist (ich habe eine starke Last auf die Anwendung und die Leistung ist kritisch).
Bisher ist mein Code:
Class<? extends Node> proxyType = new ByteBuddy()
.subclass(node.getClass(), ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC)
.method(anyOf(finalNode.getClass().getMethods())).intercept(MethodDelegation.to(NodeInterceptor.class))
.defineField("node", Node.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.defineField("groups", Map.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.defineField("rules", Set.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
enhancedClass = (Class<N>) proxyType;
EnhancedNode enhancedNode = (EnhancedNode) enhancedClass.newInstance();
enhancedNode.setNode(node);
wo Node
das Objekt zu Unterklasse/Wrap ist. Die NodeInterceptor
leitet die aufgerufenen Methoden an die getNode
-Eigenschaft weiter.
Hier ist der Code des NodeInterceptor
:
public class NodeInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method,
@This EnhancedNode proxy,
@AllArguments Object[] arguments)
throws Exception {
Node node = proxy.getNode();
Object res;
if (node != null) {
res = method.invoke(method.getDeclaringClass().cast(node), arguments);
} else {
res = null;
}
return res;
}
}
alles funktioniert, aber die Schnittverfahren ist zu langsam, ich plane ASM zu verwenden, um direkt die Implementierung jeder Methode der Knoten hinzuzufügen, aber ich hoffe, es ist ein einfacher Weg mit Byte Buddy.
Ich kenne Byte-Buddy nicht, aber ich sehe eine Chance, dass eine neue Proxy-Klasse immer wieder erstellt wird. Gibt es eine Art Caching? – JimmyB
Btw, wie erzwingst du, dass die Klasse des gegebenen Knotens einen öffentlichen Standardkonstruktor hat? – JimmyB
Sie haben eine Schnittstelle, Sie fügen Felder und Methoden hinzu ... Warum können Sie nicht einfach ohne diese dynamische Erweiterung eine Wrapper-Klasse erstellen? – AdamSkywalker