Alternative a java.lang.reflect.Proxy per la creazione di proxy di classi astratte (piuttosto che interfacce)


89

Secondo la documentazione :

[ java.lang.reflect.] Proxyfornisce metodi statici per la creazione di classi e istanze proxy dinamiche ed è anche la superclasse di tutte le classi proxy dinamiche create da questi metodi.

Il newProxyMethodmetodo (responsabile della generazione dei proxy dinamici) ha la seguente firma:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

Sfortunatamente, questo impedisce di generare un proxy dinamico che estende una specifica classe astratta (piuttosto che implementare interfacce specifiche). Questo ha senso, considerando che java.lang.reflect.Proxyè "la superclasse di tutti i proxy dinamici", impedendo così a un'altra classe di essere la superclasse.

Pertanto, esistono alternative a java.lang.reflect.Proxyche possono generare proxy dinamici che ereditano da una specifica classe astratta, reindirizzando tutte le chiamate ai metodi astratti al gestore di invocazione?

Ad esempio, supponiamo di avere una classe astratta Dog:

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

C'è una classe che mi permette di fare quanto segue?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler

Risposte:


123

Può essere fatto usando Javassist (vedi ProxyFactory) o CGLIB .

L'esempio di Adam che utilizza Javassist:

Io (Adam Paynter) ho scritto questo codice usando Javassist:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

Che produce questo output:

Trama!
Gestire il public abstract void mock.Dog.fetch () tramite il gestore del metodo

10
+1: Esattamente quello che mi serve! Modificherò la tua risposta con il mio codice di esempio.
Adam Paynter

proxyFactory.setHandler()è deprecato. Si prega di utilizzare proxy.setHandler.
AlikElzin-kilaka

@axtavt l'oggetto "Dog" è un'implementazione o un'interfaccia nel codice precedente?
stackoverflow

-7

Quello che puoi fare in questo caso è avere un gestore proxy che reindirizzerà le chiamate ai metodi esistenti della tua classe astratta.

Ovviamente dovrai codificarlo, tuttavia è abbastanza semplice. Per creare il tuo proxy, dovrai dargli un InvocationHandler. Dovrai quindi solo controllare il tipo di metodo nel invoke(..)metodo del tuo gestore di invocazioni. Ma attenzione: dovrai confrontare il tipo di metodo con l'oggetto sottostante associato al tuo gestore e non con il tipo dichiarato della tua classe astratta.

Se prendo come esempio la classe del tuo cane, il metodo invoke del tuo gestore di invocazioni potrebbe assomigliare a questo (con una sottoclasse di cani associata esistente chiamata .. beh ... dog)

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

Tuttavia, c'è qualcosa che mi fa riflettere: ho parlato di un dogoggetto. Tuttavia, poiché la classe Dog è astratta, non puoi creare istanze, quindi hai sottoclassi esistenti. Inoltre, come rivela una rigorosa ispezione del codice sorgente del Proxy, potresti scoprire (su Proxy.java:362) che non è possibile creare un Proxy per un oggetto Class che non rappresenta un'interfaccia).

Quindi, a parte la realtà , quello che vuoi fare è perfettamente possibile.


1
Per favore, abbi pazienza mentre cerco di capire la tua risposta ... Nel mio caso particolare, voglio che la classe proxy (generata in fase di esecuzione) sia la sottoclasse di Dog(ad esempio, non sto scrivendo esplicitamente una Poodleclasse che implementa fetch()). Pertanto, non esiste una dogvariabile su cui richiamare i metodi ... Scusa se sono confuso, dovrò pensarci un po 'di più.
Adam Paynter

1
@Adam - non puoi creare dinamicamente sottoclassi in fase di esecuzione senza qualche manipolazione del bytecode (CGLib penso faccia qualcosa del genere). La risposta breve è che i proxy dinamici supportano le interfacce, ma non le classi astratte, perché i due sono concetti molto diversi. È quasi impossibile pensare a un modo per proxy dinamicamente classi astratte in modo sano.
Andrzej Doyle

1
@Andrzej: capisco che quello che sto chiedendo richiede la manipolazione del bytecode (infatti, ho già scritto una soluzione al mio problema usando ASM). Capisco anche che i proxy dinamici di Java supportano solo le interfacce. Forse la mia domanda non era del tutto chiara: sto chiedendo se c'è qualche altra classe (cioè qualcosa di diverso da java.lang.reflect.Proxy) disponibile che fa ciò di cui ho bisogno.
Adam Paynter

2
Bene, per farla breve ... no (almeno nelle classi Java standard). Usando la manipolazione del bytecode, il cielo è il limite!
Riduidel

9
Ho svalutato perché non è davvero una risposta alla domanda. OP ha dichiarato di voler eseguire il proxy di una classe, non di un'interfaccia, ed è consapevole che ciò non è possibile con java.lang.reflect.Proxy. Ripeti semplicemente questo fatto e non offri altra soluzione.
jcsahnwaldt Ripristina Monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.