2009-06-01 115 views

回答

13

我強烈推薦這個resource

首先,你必須瞭解什麼是代理模式的用例。請記住,代理的主要目的是控制對目標對象的訪問,而不是增強目標對象的功能。訪問控制包括同步,身份驗證,遠程訪問(RPC),懶惰實例化(Hibernate,Mybatis),AOP(事務)。

與靜態代理相比,動態代理生成的字節碼在運行時需要Java反射。動態的你不需要創建代理類,這可以帶來更多的便利。

+0

和鏈接被破壞 – bvdb 2016-05-15 15:56:40

5

一個用例是hibernate--它爲您提供了實現您的模型類接口的對象,但是在getters和setters下面存在與db相關的代碼。即你使用它們就好像它們只是簡單的POJO一樣,但實際上有很多事情正在掩蓋之中。

例如 - 你只需調用lazily加載的屬性的getter,但真正的屬性(可能是整個大對象結構)從數據庫中獲取。

您應該檢查cglib庫以獲取更多信息。

28

動態代理類是實現在運行時指定的 接口的列表,使得通過對類的一個實例的接口 一個將被編碼和 方法調用調度到另一個類通過統一的界面對象。它可以是 ,用於爲接口列表創建類型安全的代理對象 ,無需預先生成代理類。動態代理 類對於需要提供 類型安全的對調用接口API的對象的反射調度的應用程序或庫很有用。

Dynamic Proxy Classes

+0

好的答案,這個事情的一個很好的例子是Spring Remoting,它內置了HTTP,RMI,EJB和JMS的代理功能。 – Robin 2009-06-01 13:39:53

16

我只是想出了一個動態代理一個有趣的使用。

我們遇到了一些與非關鍵服務相關的服務,這些服務與另一個相關服務結合在一起,並且希望探索在相關服務不可用時的容錯方式。

所以我寫了一個LoadSheddingProxy需要兩個代表 - 一個是'正常'服務的遠程impl(在JNDI查找之後)。另一個對象是一個「虛擬」卸載impl。圍繞每個方法調用的簡單邏輯可以捕獲超時並在重試之前轉向虛擬機一段時間。下面是我如何使用它:

// This is part of your ServiceLocator class 
public static MyServiceInterface getMyService() throws Exception 
{ 
    MyServiceInterface loadShedder = new MyServiceInterface() { 
     public Thingy[] getThingys(Stuff[] whatever) throws Exception { 
      return new Thingy[0]; 
     } 
     //... etc - basically a dummy version of your service goes here 
    }   
    Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER); 
    try { 
     MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
       ctx.lookup(MyServiceHome.JNDI_NAME), 
       MyServiceHome.class)).create(); 
     // Here's where the proxy comes in 
     return (MyService) Proxy.newProxyInstance(
      MyServiceHome.class.getClassLoader(), 
     new Class[] { MyServiceInterface.class }, 
     new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000)); // 10 minute retry 
    } catch (RemoteException e) { // If we can't even look up the service we can fail by shedding load too 
     logger.warn("Shedding load"); 
     return loadShedder; 
    } finally { 
     if (ctx != null) { 
     ctx.close(); 
     } 
    } 
} 

而這裏的代理:

public class LoadSheddingProxy implements InvocationHandler { 

static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class); 

Object primaryImpl, loadDumpingImpl; 
long retry; 
String serviceName; 
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects 
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>(); 

public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry) 
{ 
    this.serviceName = serviceName; 
    this.primaryImpl = primaryImpl; 
    this.loadDumpingImpl = loadDumpingImpl; 
    this.retry = retry; 
} 

public Object invoke(Object obj, Method m, Object[] args) throws Throwable 
{ 
    try 
    { 
     if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) { 
      Object ret = m.invoke(primaryImpl, args); 
      servicesLastTimedOut.remove(serviceName); 
      return ret; 
     } 
     return m.invoke(loadDumpingImpl, args); 
    } 
    catch (InvocationTargetException e) 
    { 
     Throwable targetException = e.getTargetException(); 

     // DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it??? 
     if (targetException instanceof RemoteException) { 
      servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis())); 
     } 
     throw targetException; 
    }      
} 

private boolean timeToRetry() { 
    long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue(); 
    return (System.currentTimeMillis() - lastFailedAt) > retry; 
} 
}