Qual è la differenza tra proxy dinamico JDK e CGLib?


147

Nel caso del Proxy Design Pattern , qual è la differenza tra il Dynamic Proxy di JDK e le API di generazione di codice dinamico di terze parti come CGLib ?

Qual è la differenza tra l'utilizzo di entrambi gli approcci e quando si dovrebbe preferire uno rispetto all'altro?


3
Ottieni il codice qui: < gist.github.com/ksauzz/1563486 >. In cglib è possibile creare sia proxy di classe che proxy di interfaccia. Spring utilizza CGlib per impostazione predefinita mentre AspectJ utilizza proxy Java. Leggi anche questo: jnb.ociweb.com/jnb/jnbNov2005.html ;)
Subhadeep Ray

Risposte:


185

Il proxy dinamico JDK può eseguire il proxy solo tramite l'interfaccia (quindi la classe target deve implementare un'interfaccia, che viene quindi implementata anche dalla classe proxy).

CGLIB (e javassist) possono creare un proxy tramite la sottoclasse. In questo scenario il proxy diventa una sottoclasse della classe target. Non sono necessarie interfacce.

Quindi i proxy dinamici Java possono eseguire il proxy: public class Foo implements iFoo dove CGLIB può eseguire il proxy:public class Foo

MODIFICARE:

Devo dire che, poiché javassist e CGLIB usano il proxy tramite la sottoclasse, questo è il motivo per cui non è possibile dichiarare i metodi finali o rendere la classe definitiva quando si utilizzano framework che si basano su questo. Ciò impedirebbe a queste librerie di consentire la sottoclasse della classe e la sostituzione dei metodi.


Grazie..!! ma sarebbe utile se tu potessi darmi un codice di esempio (o Link) per illustrare il proprio utilizzo su un altro in qualche caso .. !!!
KDjava,

1
Si noti che i proxy JDK stanno effettivamente battendo il proxy per IFoo per nessun tipo di Foo. È una distinzione piuttosto importante. Inoltre, i proxy cglib sono sottoclassi complete: approfittane! Usa i filtri solo per i metodi proxy che ti interessano e usa direttamente la classe generata.
lscoughlin,

9
Va anche notato che la creazione della sottoclasse CGLib richiede la conoscenza sufficiente della superclasse per poter chiamare il costruttore corretto con gli arg giusti. A differenza del proxy basato su interfaccia a cui non interessano i costruttori. Questo rende il lavoro con i proxy CGLib meno "automatico" dei proxy JDK. Un'altra distinzione è nel costo dello "stack". Un proxy JDK comporta sempre frame di stack aggiuntivi per chiamata mentre un CGLib non può costare frame di stack aggiuntivi. Questo diventa sempre più rilevante quanto più l'app diventa complessa (poiché più grande è lo stack, maggiore è il consumo di thread di memoria).
Ray

1
cglib non può eseguire il proxy dei metodi finali, ma non genererà
Muhammad Hewedy,

Sì, CGLIB ignora semplicemente i metodi finali.
yashjain12yj

56

Differenze di funzionalità

  • I proxy JDK consentono di implementare qualsiasi set di interfacce durante la sottoclasse Object. Qualsiasi metodo di interfaccia, più Object::hashCode, Object::equalse Object::toStringviene quindi inoltrato a un InvocationHandler. Inoltre, java.lang.reflect.Proxyè implementata l' interfaccia della libreria standard .

  • cglib consente di implementare qualsiasi set di interfacce durante la sottoclasse di qualsiasi classe non finale. Inoltre, i metodi possono essere sovrascritti facoltativamente, ovvero non tutti i metodi non astratti devono essere intercettati. Inoltre, esistono diversi modi per implementare un metodo. Offre anche una InvocationHandlerclasse (in un pacchetto diverso), ma consente anche di chiamare super metodi usando intercettori più avanzati come ad esempio a MethodInterceptor. Inoltre, cglib può migliorare le prestazioni mediante intercettazioni specializzate come FixedValue. Una volta ho scritto un riassunto di diversi intercettori per cglib .

Differenze di prestazione

I proxy JDK sono implementati in modo piuttosto ingenuo con un solo dispatcher di intercettazione, il InvocationHandler. Ciò richiede l'invio di un metodo virtuale a un'implementazione che non può sempre essere integrata. Cglib consente di creare un codice byte specializzato che a volte può migliorare le prestazioni. Ecco alcuni confronti per l'implementazione di un'interfaccia con 18 metodi stub:

            cglib                   JDK proxy
creation    804.000     (1.899)     973.650     (1.624)
invocation    0.002     (0.000)       0.005     (0.000)

Il tempo è indicato in nanosecondi con deviazione standard tra parentesi graffe. Puoi trovare maggiori dettagli sul benchmark nel tutorial di Byte Buddy, in cui Byte Buddy è un'alternativa più moderna a cglib. Inoltre, nota che cglib non è più in fase di sviluppo attivo.


2
Perché la documentazione di primavera favorisce il proxy di JDK su cglib, visti i vantaggi in termini di prestazioni di quest'ultimo? docs.spring.io/spring/docs/2.5.x/reference/…
P4ndaman

2
Cglib è una dipendenza esterna e attualmente non supportata. Affidarsi a software di terze parti è sempre una scommessa, quindi è meglio quando il minor numero di persone possibile fa affidamento su di esso.
Rafael Winterhalter,

Nel tuo blog dici: "Tuttavia, dovresti stare attento quando chiami un metodo sull'oggetto proxy che viene fornito con il metodo InvocationHandler # invoke. Tutte le chiamate su questo metodo verranno inviate con lo stesso InvocationHandler e potrebbero quindi generare un ciclo infinito ". Cosa intendi?
Koray Tugay,

Se chiami un metodo sull'oggetto proxy, qualsiasi chiamata viene instradata attraverso il nostro gestore di invocazione. Se un gestore di chiamata chiama un delegato a una chiamata all'oggetto, si verifica la ricorsione menzionata.
Rafael Winterhalter,

Ciao Rafael, messaggio estraneo alla tua risposta, ti sto inviando un ping per una modifica fatta 5 anni fa . Poiché apparentemente cglib si è ancora impegnato nel 2019 e non mostra alcun sviluppo arrestato nel suo file Leggimi, ho rimosso la tua dichiarazione dall'estratto del tag. Sentiti libero di migliorare la descrizione del tag / estratto se c'è qualcosa di rilevante da menzionare.
Cor

28

Proxy dinamico: implementazioni dinamiche di interfacce in fase di esecuzione utilizzando l' API di riflessione JDK .

Esempio: Spring utilizza proxy dinamici per le transazioni come segue:

inserisci qui la descrizione dell'immagine

Il proxy generato viene aggiunto al bean. Aggiunge un comportamento transnazionale al bean. Qui il proxy viene generato dinamicamente in fase di esecuzione utilizzando l'API Reflection JDK.

Quando un'applicazione viene arrestata, il proxy verrà distrutto e avremo solo interfaccia e bean sul file system.


Nell'esempio sopra abbiamo l'interfaccia. Ma nella maggior parte dell'implementazione dell'interfaccia non è la cosa migliore. Quindi il bean non implementa un'interfaccia, in quel caso usiamo l'ereditarietà:

inserisci qui la descrizione dell'immagine

Per generare tali proxy, Spring utilizza una libreria di terze parti chiamata CGLib .

CGLib ( C ode G eneration Lib rary) è basato su ASM , questo viene principalmente utilizzato per generare il bean che estende il proxy e aggiunge il comportamento del bean nei metodi proxy.

Esempi per proxy dinamico JDK e CGLib

Rif. Primavera


5

Dalla documentazione di primavera :

Spring AOP utilizza proxy dinamici JDK o CGLIB per creare il proxy per un determinato oggetto di destinazione. (I proxy dinamici JDK sono preferiti ogni volta che hai una scelta).

Se l'oggetto target da sottoporre a proxy implementa almeno un'interfaccia, verrà utilizzato un proxy dinamico JDK. Tutte le interfacce implementate dal tipo di destinazione verranno inviate in proxy. Se l'oggetto target non implementa alcuna interfaccia, verrà creato un proxy CGLIB.

Se si desidera forzare l'uso del proxy CGLIB (ad esempio, per eseguire il proxy di tutti i metodi definiti per l'oggetto di destinazione, non solo quelli implementati dalle sue interfacce), è possibile farlo. Tuttavia, ci sono alcuni problemi da considerare:

i metodi finali non possono essere consigliati, in quanto non possono essere sostituiti.

Avrai bisogno dei binari CGLIB 2 sul tuo percorso di classe, mentre i proxy dinamici sono disponibili con JDK. Spring ti avviserà automaticamente quando è necessario CGLIB e le classi della libreria CGLIB non si trovano sul percorso di classe.

Il costruttore del tuo oggetto proxy verrà chiamato due volte. Questa è una conseguenza naturale del modello proxy CGLIB in base al quale viene generata una sottoclasse per ciascun oggetto proxy. Per ogni istanza proxy, vengono creati due oggetti: l'oggetto proxy effettivo e un'istanza della sottoclasse che implementa il consiglio. Questo comportamento non viene mostrato quando si utilizzano proxy JDK. Di solito, chiamare il costruttore del tipo proxy due volte non è un problema, poiché di solito ci sono solo assegnazioni in corso e nessuna logica reale è implementata nel costruttore.

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.