2010-03-29 100 views
12

Java中是否有任何技術可用於攔截消息(方法調用),比如Ruby中的method_missing技術?這將允許編碼裝飾和代理很容易 ,像紅寶石:缺少裝飾的Java方法(ala 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的())

回答

17

正如其他人已經正確表示,使用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(); 
    } 
} 
+0

@ killdash10:謝謝,非常有幫助! – cibercitizen1 2010-03-30 12:51:41

+0

這是我見過的最棒的東西。 – 2013-04-10 21:58:28

+0

「代理實現的接口上的所有方法調用」 - 因此,如果要攔截*任何可能的*方法調用,那麼該方法無用嗎? – allquixotic 2017-09-24 23:19:48

0

不完全是。最接近的對象是一個Dynamic Proxy對象,但有一些限制(即只能通過反射調用它)。

+0

是什麼給了你這個想法?代理對象可以像任何其他對象一樣進行強制轉換和調用。 – skaffman 2010-03-29 22:04:07

2

請參閱java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler或面向方面的編程(例如AspectJ)。

4

java.lang.reflect.Proxy是爲您在運行時指定的接口生成運行時代理的起點。它允許你指定要被代理的接口,以及處理「真實」調用的對象,如果你選擇的話,當然可以簡單地調用別的東西。

Proxy類的輸出是一個對象,您可以將其轉換爲所需的接口類型,並像使用其他對象一樣使用和調用。

它不會像使用Ruby這樣的動態語言那麼容易,但是您要爲Java這樣的強靜態語言付出代價。