2010-03-29 21 views
12

Czy jest dostępna technika Java w celu przechwytywania wiadomości (wywołania metod), takich jak technika method_missing w Ruby? Pozwoliłoby to kodowanie dekoratorów i prokurentów bardzo łatwo, jak w Ruby:Brakuje metody Java (ala Ruby) do dekoracji?

: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 

(Uwaga: Pełnomocnik ma tylko jedną metodę: method_missing())

Odpowiedz

17

Jak już mówili inni, należy użyć DynamicProxy. Oto przykład.

Ta klasa używa DynamicProxy do przechwytywania wywołań metod zadeklarowanych w interfejsie "HammerListener". Wykonuje pewne logowanie, a następnie deleguje do "prawdziwej" implementacji HammerListener (tak, to samo można zrobić z AOP).

Zobacz metodę newInstance dla instancji proxy (pamiętaj, że musisz przekazać interfejs (y), który serwer proxy powinien zaimplementować - proxy może zaimplementować wiele interfejsów).

Wszystkie wywołania metod na interfejsach, które implementuje proxy, zostaną zakończone jako wywołania metody "invoke", która jest zadeklarowana w interfejsie "InvocationHandler". Wszystkie procedury obsługi proxy muszą implementować ten interfejs.

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: Dzięki, bardzo pomocne! – cibercitizen1

+0

To jest najbardziej niesamowita rzecz, jaką widziałem od jakiegoś czasu. –

+0

"Wszystkie wywołania metod na interfejsach implementowanych przez serwer proxy" - A więc nie jest użyteczne, jeśli chcesz przechwycić * możliwe wywołanie metody *, a następnie? – allquixotic

0

Niezupełnie. Najbliższym odpowiednikiem jest obiekt Dynamic Proxy, ale ma on pewne ograniczenia (tzn. Można go wywoływać tylko poprzez odbicie).

+0

Co dało ci ten pomysł? Obiekt proxy może być rzutowany i wywoływany jak każdy inny obiekt. – skaffman

2

Zobacz java.lang.reflect.Proxy i java.lang.reflect.InvocationHandler lub ogólnie programowanie (na przykład AspectJ).

4

java.lang.reflect.Proxy to punkt wyjścia do generowania proxy środowiska wykonawczego dla interfejsów określonych w czasie wykonywania. Pozwala określić interfejs, który ma być proxy, oraz obiekt, który obsługuje "prawdziwą" inwokację, która, jeśli wybierzesz, może oczywiście po prostu wywołać coś innego.

Dane wyjściowe klasy Proxy to obiekt, który można przesłać do żądanego typu interfejsu, a następnie użyć i wywołać jak każdy inny obiekt.

To nie będzie tak proste, jak w przypadku dynamicznego języka, takiego jak Ruby, ale płacisz cenę za bardzo statyczny język, taki jak Java.