Il problema è creare una versione potenziata dinamica di oggetti esistenti.Come implementare un decoratore wrapper in Java?
Non riesco a modificare l'oggetto Class
. Invece devo:
- sottoclasse esso
- avvolgere l'oggetto esistente nel nuovo
Class
- delegato tutto il metodo originale prevede all'oggetto avvolto
- implementare tutti i metodi che sono definiti da un'altra interfaccia
L'interfaccia di aggiungere oggetti esistenti è:
public interface EnhancedNode {
Node getNode();
void setNode(Node node);
Set getRules();
void setRules(Set rules);
Map getGroups();
void setGroups(Map groups);
}
Con Byte Buddy Sono riuscito a creare sottoclasse e implementare la mia interfaccia. Il problema è la delega all'oggetto spostato. L'unico modo per fare ciò che ho trovato è l'uso della riflessione, cosa troppo lenta (ho un carico pesante sull'applicazione e le prestazioni sono fondamentali).
Finora il mio codice è:
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);
dove Node
è l'oggetto di sottoclasse/involucro. Lo NodeInterceptor
inoltra i metodi richiamati alla proprietà getNode
.
Ecco il codice del 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;
}
}
Tutto sta funzionando, ma il metodo di intercettazione è troppo lento, ho intenzione di utilizzare ASM direttamente per aggiungere l'attuazione di tutti i metodi di Node, ma ci spero è un modo più semplice usando Byte Buddy.
Non conosco Byte-Buddy, ma vedo la possibilità che una nuova classe di proxy venga creata più e più volte. C'è qualche tipo di memorizzazione nella cache? – JimmyB
Btw, come si fa a rispettare il fatto che la classe del nodo specificato abbia un costruttore pubblico predefinito? – JimmyB
Hai un'interfaccia, aggiungi campi e metodi ... Perché non puoi semplicemente creare una classe wrapper da solo senza questo miglioramento dinamico? – AdamSkywalker