2013-11-03 6 views
6

Forse non sto pensando abbastanza o la risposta è davvero elusiva. Scenario rapido (prova il codice. Compila).CGLIB non è in grado di intercettare i metodi in una superclasse/superinterfaccia

consideri un'interfaccia legacy

public interface LegacyInterfaceNoCodeAvailable{ 
    void logInfo(String message); 
} 

Il prendere in considerazione un'implementazione eredità di interfaccia di cui sopra

public abstract class LegacyClassNoCodeAvailable implements LegacyInterfaceNoCodeAvailable{ 

    public abstract void executeSomething(); 

    public void rockItOldSchool(){ 
     logInfo("bustin' chops, old-school style"); 
    } 

    @Override 
    public void logInfo(String message){ 
     System.out.println(message); 
    } 
} 

Ora vengo come questa persona ambiziosa e scrive una classe per il sistema di un 'Nuovo', ma che corre all'interno del framework "Legacy", quindi devo estendere la classe base legacy.

public class lass SpankingShiny extends LegacyClassNoCodeAvailable{ 

    public void executeSomething(){ 
     rockItOldSchool(); 
     logInfo("I'm the King around here now"); 
     System.out.println("this new stuff rocks!!"); 
    } 
} 

Tutto funziona alla grande, proprio come ci si aspetterebbe:

SpankingShiny shiny = new SpankingShiny(); 
shiny.executeSomething(); 

I rendimenti superiori del codice (come previsto):

bustin' chops, old-school style 
I'm the King around here now 
this new stuff rocks!! 

Ora, come potete vedere, il 'sistema. out.println() 'stampa fedelmente l'output desiderato. Ma desidero sostituire "System.out.println()" con un programma di registrazione.

Problema:

Sono in grado di avere la delega CGLIB intercetta il metodo per 'loginfo (stringa)' e farlo stampare il mio messaggio desiderato attraverso un logger (ho fatto la configurazione di registrazione direttamente sul modo). L'invocazione del metodo "apparentemente" non colpisce il proxy.

Codice:

public class SpankingShinyProxy implements MethodInterceptor{ 

    private SpankingShiny realShiny; 
    private final Logger logger = Logger.getLogger(SpankingShinyProxy.class); 

    public SpankingShinyProxy(SpankingShiny realShiny) { 
     super(); 
     this.realShiny = realShiny; 
    } 

    @Override 
    public Object intercept(Object proxyObj, Method proxyMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable { 
     String methodName = proxyMethod.getName(); 
     if("logInfo".equals(methodName)){ 
      logger.info(methodParams[0]); 
     } 
     return proxyMethod.invoke(realShiny, methodParams); 
    } 

    public static SpankingShiny createProxy(SpankingShiny realObj){ 
     Enhancer e = new Enhancer(); 
     e.setSuperclass(realObj.getClass()); 
     e.setCallback(new SpankingShinyProxy(realObj)); 
     SpankingShiny proxifiedObj = (SpankingShiny) e.create(); 
     return proxifiedObj; 
    } 
} 

metodo Main:

public static void main(String... args) { 

     SpankingShiny shiny = new SpankingShiny(); 
     shiny.executeSomething(); 

     SpankingShiny shinyO = SpankingShinyProxy.createProxy(shiny); 
     shinyO.executeSomething(); 
    } 

I rendimenti del codice di cui sopra (non come previsto):

bustin' chops, old-school style 
I'm the King around here now 
this new stuff rocks!! 
bustin' chops, old-school style 
I'm the King around here now 
this new stuff rocks!! 

Dove dovrei andare male?

Grazie!

risposta

1

Beh, prima di tutto, sei fortunato che il tuo proxy non venga colpito. Se si stesse facendo riferimento al proxy effettivo all'interno di intercept, si finirebbe con un ciclo infinito poiché il proprio metodo di incisione riflessivo verrebbe inviato dallo stesso SpankingShinyProxy. Ancora e ancora.

Il proxy non funziona poiché è sufficiente delegare la chiamata del metodo executeSomething al proprio proxy a un oggetto non associato. Non devi usare realObj. Tutte le chiamate al metodo devono essere inviate dal tuo proxy, anche quelle chiamate di metodo invocate dal deve colpire il proxy stesso!

Modificare l'ultima riga nel metodo intercept in methodProxy.invokeSuper(proxyObj, args). Quindi, costruisci il tuo oggetto usando lo Enhancer. Se il tuo costruttore per SpankingShiny non ha bisogno di argomenti, chiama create senza argomenti se lo è. Altrimenti, fornire gli oggetti che normalmente forniresti al costruttore nel metodo create. Quindi, usa solo l'oggetto che ottieni da create e sei a posto.

Se volete maggiori informazioni su cglib, si potrebbe desiderare di leggere questo articolo del blog: http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

0

Ho avuto lo stesso problema. Nel mio caso, il realObj era un proxy stesso (un Spring Bean - un @Component).

Quindi quello che dovevo fare era cambiare la parte .setSuperClass() in:

Enhancer e = new Enhancer(); 
e.setSuperclass(realObj.getClass()); 
e.setCallback(new SpankingShinyProxy(realObj)); 
SpankingShiny proxifiedObj = (SpankingShiny) e.create(); 

ho cambiato:

e.setSuperclass(realObj.getClass()); 

A:

e.setSuperclass(realObj.getClass().getSuperClass()); 

Questo ha funzionato perché, come detto, realObj.getClass() era un proxy CGLIB stesso e quel metodo restituiva un clas pazzo-nome-generato da CGLIB s, ad esempio a.b.c.MyClass$$EnhancerBySpringCGLIB$$1e18666c. Quando ho aggiunto .getSuperClass() ha restituito la classe che avrebbe dovuto restituire in primo luogo.

Problemi correlati