2010-03-29 852 views
12

¿Hay alguna técnica disponible en Java para interceptar mensajes (llamadas a métodos) como la técnica method_missing en Ruby? Esto permitiría a los decoradores y proxies de codificación muy fácilmente, como en Ruby:Falta el método Java (ala Ruby) para decorar?

: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 sólo tiene un método: method_missing())

Respuesta

17

Como ya se ha dicho correctamente, use un DynamicProxy. Aquí hay un ejemplo.

Esta clase usa un DynamicProxy para interceptar invocaciones de métodos declarados en la interfaz "HammerListener". Hace un poco de registro y luego delega en la implementación "real" de HammerListener (sí, lo mismo se puede hacer con AOP).

Vea el nuevo método de instancia para la creación de instancias de proxy (tenga en cuenta que debe pasar las interfaces que el proxy debe implementar: un proxy puede implementar una interfaz múltiple).

Todas las invocaciones de métodos en las interfaces que implementa el proxy terminarán como llamadas al método "invocar", que se declara en la interfaz "InvocationHandler". Todos los controladores proxy deben implementar esta interfaz.

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: ¡Gracias, muy útiles! – cibercitizen1

+0

Esto es lo más impresionante que he visto en mucho tiempo. –

+0

"Todas las invocaciones de métodos en las interfaces que implementa el proxy" - Entonces, ¿no es útil si quiere interceptar * cualquier posible invocación de método? – allquixotic

0

No exactamente. El equivalente más cercano es un objeto Dynamic Proxy, pero eso tiene algunas limitaciones (es decir, solo se puede llamar a través de la reflexión).

+0

¿Qué le dio esa idea? El objeto proxy puede ser invocado y lanzado como cualquier otro objeto. – skaffman

2

Consulte java.lang.reflect.Proxy y java.lang.reflect.InvocationHandler o la programación orientada a aspectos en general (AspectJ por ejemplo).

4

java.lang.reflect.Proxy es el punto de partida para generar proxies en tiempo de ejecución para las interfaces que especifique en tiempo de ejecución. Le permite especificar la interfaz a ser proxiada, así como el objeto que maneja la invocación "real" que, si lo desea, puede simplemente llamar a otra cosa.

La salida de la clase Proxy es un objeto que puede convertir al tipo de interfaz que desee e invocar como cualquier otro objeto.

No va a ser tan fácil como con un lenguaje dinámico como Ruby, pero pagas un precio por un lenguaje fuertemente estático como Java.

Cuestiones relacionadas