Java метод отсутствует (аля Ruby) для украшения? - PullRequest
12 голосов
/ 30 марта 2010

Существует ли в Java какой-либо метод перехвата сообщений (вызовов методов), например, метод method_missing в Ruby?Это позволило бы очень легко кодировать декораторы и прокси, как в Ruby:

: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

(Примечание: прокси имеет только один метод: method_missing ())

Ответы [ 4 ]

17 голосов
/ 30 марта 2010

Как уже правильно сказали другие, используйте DynamicProxy. Вот пример.

Этот класс использует DynamicProxy для перехвата вызовов методов, объявленных в интерфейсе "HammerListener". Он выполняет некоторую регистрацию и затем делегирует «настоящую» реализацию HammerListener (да, то же самое можно сделать с AOP).

См. Метод newInstance для создания экземпляров прокси (обратите внимание, что вам нужно передать интерфейс (ы), которые должен реализовывать прокси - прокси может реализовать несколько интерфейсов).

Все вызовы методов на интерфейсах, которые реализует прокси, будут заканчиваться вызовами метода invoke, который объявлен в интерфейсе InvocationHandler. Все обработчики прокси должны реализовывать этот интерфейс.

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();
     }
}
4 голосов
/ 30 марта 2010

java.lang.reflect.Proxy - это отправная точка для создания прокси времени выполнения для интерфейсов, которые вы указываете во время выполнения. Он позволяет вам указать интерфейс для прокси, а также объект, который обрабатывает «реальный» вызов, который, если вы выберете, может, конечно, просто вызвать что-то еще.

Выход класса Proxy - это объект, который вы можете привести к желаемому типу интерфейса, а также использовать и вызывать, как и любой другой объект.

Это будет не так просто, как с динамическим языком, таким как Ruby, но вы платите цену за сильно статический язык, такой как Java.

2 голосов
/ 30 марта 2010

См. Java.lang.reflect.Proxy и java.lang.reflect.InvocationHandler или Aspect-ориентированное программирование в целом (например, AspectJ).

0 голосов
/ 30 марта 2010

Не совсем. Ближайшим эквивалентом является объект Dynamic Proxy , но он имеет некоторые ограничения (т. Е. Его можно вызывать только с помощью отражения).

...