代理模式:對其他對象提供一種代理以控制對這個對象的訪問。代理模式的主要作用是為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。代理模式的思想是為了提供額外的處理或者不同的操作而在實際對象與調用者之間插入一個代理對象。
代理模式中一般涉及4中角色:
InterfaceSubject :該接口是對被訪問者或者被訪問資源的抽象。
SubjectImpl:被訪問者或者被訪問資源的具體實現類,實現了InterfaceSubject
SubjectProxy:被訪問者或者被訪問資源的代理實現類,該類持有一個InterfaceSubject接口的實例
Client:代表訪問者的抽象角色,Client將會訪問InterfaceSubject類型的對象或者資源,在這個情景中,Client無法直接訪問到InterfaceSubject資源,而是通過代理類SubjectProxy去實現。下面是一個簡單的示例:
public interface ProxyISubject { String request(); }
public class ProxySubjectImpl implements ProxyISubject{ @Override public String request() { return "OK"; // TODO Auto-generated method stub } }
public class ProxySubjectProxy implements ProxyISubject{ private static final Log logger = LogFactory.getLog(ProxySubjectProxy.class); private ProxyISubject proxyISubject; @Override public String request() { // TODO Auto-generated method stub TimeOfDay startTime = new TimeOfDay(0,0,0); TimeOfDay endTime = new TimeOfDay(5,59,59); TimeOfDay currentTime = new TimeOfDay(); if(currentTime.isAfter(startTime)&¤tTime.isBefore(endTime)) return null; String result = proxyISubject.request(); return "Proxy:" + result; } public void LogSubjectProxy(ProxyISubject iSubject){ this.proxyISubject = iSubject; } public ProxyISubject getProxyISubject() { return proxyISubject; } public void setProxyISubject(ProxyISubject proxyISubject) { this.proxyISubject = proxyISubject; } }
public class ProxyClient{ @Test public void TestProxy(){ ProxySubjectProxy proxy = new ProxySubjectProxy(); proxy.setProxyISubject(new ProxySubjectImpl()); //proxy.setProxyISubject((ProxyISubject)Proxy.newProxyInstance(ProxySubjectProxy.class.getClassLoader(), new Class[] {ProxyISubject.class}, new DynamicProxyRequestCtrlInvocationHandler(new ProxySubjectImpl()))); assertEquals("Proxy:OK", proxy.request()); } }
靜態代理模式的缺點:對於相同的橫切邏輯,如果要插入到不同的目標當中的時候,需要重新去實現Proxy代理類。
動態代理:在JDK1.3之后引入了一種稱之為動態代理的機制,使用該機制,我們可以指定的接口在系統運行期間動態地生成代理對象,動態代理機制的實現主要有一個類和一個接口組成,即reflect.Proxy 和 reflect.InvocationHandler.
其中InvocationHandler就是我們實現橫切邏輯的地方,作用跟Advice一樣。缺陷:動態代理機制只能對實現了相應接口的類使用,因此在默認的情況下,如果對象實現了某個接口,這采用動態代理機制為某個生成代理對象實現,如果沒有,這使用一種CGLIB的動態字節碼生成類庫,為目標生成動態代理實例。
InvocationHandler示例:
public class DynamicProxyRequestCtrlInvocationHandler implements InvocationHandler{ private static final Log logger = LogFactory.getLog(DynamicProxyRequestCtrlInvocationHandler.class); private Object target; public DynamicProxyRequestCtrlInvocationHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub if(method.getName().equals("request")){ TimeOfDay startTime = new TimeOfDay(0,0,0); TimeOfDay endTime = new TimeOfDay(5,59,59); TimeOfDay currentTime = new TimeOfDay(); if(currentTime.isAfter(startTime)&¤tTime.isBefore(endTime)){ logger.debug("the time is error"); return null; } logger.info("the time is right"); return method.invoke(target, args); } return null; } }