2010-03-29 13 views
12

Esiste qualche tecnica disponibile in Java per intercettare messaggi (chiamate di metodo) come la tecnica method_missing in Ruby? Ciò consentirebbe di codifica decoratori e proxy molto facilmente, come in Ruby:Manca il metodo Java (ala Ruby) per la decorazione?

:Client   p:Proxy     im:Implementation 
-------   ----------     ----------------- 

p.foo() -------> method_missing() 
        do_something 
        im.foo() ------------------> do_foo 


p.bar() --------> method_missing() 
        do_something_more 
        im.bar() -------------------> do_bar 

(Nota: proxy ha un solo metodo: method_missing())

risposta

17

Come altri hanno già detto correttamente, utilizzare un DynamicProxy. Ecco un esempio.

Questa classe utilizza un DynamicProxy per intercettare le invocazioni dei metodi dichiarati nell'interfaccia "HammerListener". Fa un po 'di logging e quindi delega all'implementazione "reale" di HammerListener (sì, la stessa cosa può essere fatta con AOP).

Vedere il nuovo metodoInstance per l'istanziazione proxy (si noti che è necessario passare l'interfaccia (s) che il proxy deve implementare - un proxy può implementare un'interfaccia multipla).

Tutte le chiamate di metodi sulle interfacce che il proxy implementa finiranno come chiamate al metodo "invoke", che viene dichiarato nell'interfaccia "InvocationHandler". Tutti i gestori di proxy devono implementare questa interfaccia.

import java.lang.reflect.*; 

/** 
* Decorates a HammerListener instance, adding BEFORE/AFTER 
* log messages around all methods exposed in the HammerListener interface. 
*/ 

public class HammerListenerDecorator implements InvocationHandler { 

    private final HammerListener delegate; 

    static HammerListener newInstance(HammerListener delegate) { 
     ClassLoader cl = Thread.currentThread().getContextClassLoader(); 
     return (HammerListener)Proxy.newProxyInstance(cl, new Class[]{HammerListener.class}, 
      new HammerListenerDecorator(delegate)); 
    } 

    private HammerListenerDecorator(HammerListener delegate) { 
     this.delegate = delegate; 
    } 

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
     logger.info("BEFORE " + method.getName() + " {{{" + argsToString(args) + "}}}"); 
     Object rtn = method.invoke(delegate, args); 
     logger.info("AFTER " + method.getName()); 
     return rtn; 
    } 

    private String argsToString(Object[] args) { 
     StringBuilder sb = new StringBuilder(); 
     for (Object o : args) { 
      sb.append(String.valueOf(o)).append(" "); 
     } 
     return sb.toString(); 
    } 
} 
+0

@ killdash10: Grazie, molto utile! – cibercitizen1

+0

Questa è la cosa più straordinaria che ho visto da un po '. –

+0

"Tutte le chiamate di metodi sulle interfacce implementate dal proxy" - Quindi, non utile se si desidera intercettare * qualsiasi richiamo del metodo * possibile, quindi? – allquixotic

0

Non esattamente. L'equivalente più vicino è un oggetto Dynamic Proxy, ma presenta alcune limitazioni (ad esempio, può essere chiamato solo tramite riflessione).

+0

Cosa ti ha dato quell'idea? L'oggetto proxy può essere lanciato e invocato come qualsiasi altro oggetto. – skaffman

2

Vedere java.lang.reflect.Proxy e java.lang.reflect.InvocationHandler o programmazione orientata all'aspetto in generale (AspectJ per esempio).

4

java.lang.reflect.Proxy è il punto di partenza per la generazione di proxy di runtime per le interfacce specificate in fase di esecuzione. Permette di specificare l'interfaccia da proxy, così come l'oggetto che gestisce l'invocazione "reale" che, se si sceglie, può semplicemente chiamare qualcos'altro.

L'output della classe Proxy è un oggetto che è possibile trasmettere al tipo di interfaccia desiderato e utilizzare e richiamare come qualsiasi altro oggetto.

Non sarà facile come con un linguaggio dinamico come Ruby, ma si paga un prezzo per un linguaggio fortemente statico come Java.