注意:全文是基于Java來(lái)描述和實(shí)現(xiàn)的闹究!
代理模式的實(shí)現(xiàn)有很多種方法:靜態(tài)代理姑原,動(dòng)態(tài)代理(又分為反射實(shí)現(xiàn)的動(dòng)態(tài)代理,CGLib通過(guò)修改字節(jié)碼文件實(shí)現(xiàn)的動(dòng)態(tài)代理)涧狮。代理模式的目的是解耦Client和Subject酪碘,Proxy作為Client和Subject之間的中間人接受Client的操作請(qǐng)求并和Subject交互朋譬。通過(guò)這種Proxy的方式可以在原操作的基礎(chǔ)上添加額外的功能和操作,比如對(duì)數(shù)據(jù)庫(kù)的讀寫(xiě)操作結(jié)束之后對(duì)讀寫(xiě)結(jié)果做緩存處理兴垦,比如對(duì)指定的操作之前和之后做日志的記錄徙赢。
靜態(tài)代理
代理模式(靜態(tài))的類(lèi)圖如下:
Java實(shí)現(xiàn)如下:
Subject接口
interface Subject{
void request();
}
RealSubject類(lèi)
class RealSubject implements Subject{
public void request(){
System.out.println("RealSubject request invoke!");
}
}
Proxy類(lèi)
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
private void preRequest(){
System.out.println("preRequest");
}
private void postRequest(){
System.out.println("postRequest");
}
public void request(){
preRequest();
subject.request();
postRequest();
}
}
Client類(lèi)
class Client{
public static void main(String [] args){
Proxy subjectProxy = new Proxy(new RealSubject());
subjectProxy.request();
}
}
Proxy的作用就是充當(dāng)Client和Subject的中間人字柠,告訴Proxy要找Subject做什么,然后Proxy會(huì)負(fù)責(zé)整個(gè)調(diào)用流程狡赐,包括調(diào)用前調(diào)用后甚至出現(xiàn)Exception的時(shí)候要做的額外的工作窑业。
舉個(gè)更容易理解的例子:代購(gòu)。Client是買(mǎi)家枕屉,Proxy代購(gòu)者常柄,Subject是賣(mài)家。Client只需要告訴Proxy想買(mǎi)什么產(chǎn)品搀擂,而不用管這個(gè)產(chǎn)品在怎么買(mǎi)(是online還是線下)和去哪兒買(mǎi)(是本國(guó)還是要出國(guó))西潘,整個(gè)中間所有的細(xì)節(jié)都由Proxy來(lái)負(fù)責(zé),而在Subject那邊買(mǎi)東西只是整個(gè)購(gòu)買(mǎi)流程的一部分哨颂。
如上就是一個(gè)簡(jiǎn)單的代理模式的實(shí)現(xiàn)喷市,也是靜態(tài)代理的實(shí)現(xiàn)。靜態(tài)代理模式下威恼,如果要針對(duì)其他操作提供代理品姓,就需要針對(duì)不同的Subject接口實(shí)現(xiàn)不同的Proxy代理類(lèi)。如果代理很多箫措,會(huì)造成高代碼重復(fù)率腹备。
舉一個(gè)實(shí)際應(yīng)用的例子,你想通過(guò)代理為數(shù)據(jù)庫(kù)讀寫(xiě)操作加上緩存機(jī)制蒂破,在讀寫(xiě)操作結(jié)束后將結(jié)果緩存到內(nèi)存中馏谨,數(shù)據(jù)庫(kù)讀寫(xiě)操作如果都要如上述實(shí)現(xiàn)靜態(tài)代理就太啰嗦了别渔。解決這個(gè)問(wèn)題的一個(gè)方法是使用通過(guò)Java的反射機(jī)制實(shí)現(xiàn)的動(dòng)態(tài)代理來(lái)代替靜態(tài)代理附迷。
動(dòng)態(tài)代理
interface Subject{
void request();
}
RealSubject類(lèi)
class RealSubject implements Subject{
public void request(){
System.out.println("RealSubject request invoke!");
}
}
動(dòng)態(tài)代理類(lèi)實(shí)現(xiàn):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy implements InvocationHandler {
Object tar;
public Object bind(Object tar) {
this.tar = tar;
return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// operations before call the method
System.out.println("print before dynamic proxy invoke method of target");
return method.invoke(tar, args);
// operations after call the method
System.out.println("print before dynamic proxy invoke method of target");
}
}
調(diào)用代碼:
class Client{
public static void main(String [] args){
Subject vender = (Subject) new DynamicProxy().bind(new RealSubject());
vender.request();
}
}
針對(duì)不同的接口Subject,不需要再提供不同的Proxy代理類(lèi)的實(shí)現(xiàn)哎媚,只需要在通過(guò)new DynamicProxy()
創(chuàng)建代理的時(shí)候傳入實(shí)現(xiàn)了該接口的instance就好喇伯。
可以看出,Java通過(guò)反射機(jī)制實(shí)現(xiàn)動(dòng)態(tài)代理需要被代理對(duì)象實(shí)現(xiàn)統(tǒng)一的接口拨与,如果想代理沒(méi)有實(shí)現(xiàn)特定接口的對(duì)象怎么辦呢稻据?這時(shí)可以考慮使用CGLib實(shí)現(xiàn)的動(dòng)態(tài)代理模式。