簡(jiǎn)介
在某些情況下,我們不希望或是不能直接訪問(wèn)對(duì)象 A,而是通過(guò)訪問(wèn)一個(gè)中介對(duì)象 B,由 B 去訪問(wèn) A 達(dá)成目的,這種方式我們就稱為代理。
這里對(duì)象 A 所屬類我們稱為委托類,也稱為被代理類,對(duì)象 B 所屬類稱為代理類。
代理優(yōu)點(diǎn)有:
- 隱藏委托類的實(shí)現(xiàn)
- 解耦,不改變委托類代碼情況下做一些額外處理,比如添加初始判斷及其他公共操作
根據(jù)程序運(yùn)行前代理類是否已經(jīng)存在,可以將代理分為靜態(tài)代理和動(dòng)態(tài)代理。
動(dòng)態(tài)代理實(shí)現(xiàn)方式主要有兩種,分別是JDK動(dòng)態(tài)代理和CGlib動(dòng)態(tài)代理。二者的主要區(qū)別是JDK動(dòng)態(tài)代理是通過(guò)反射,只能代理實(shí)現(xiàn)接口的目標(biāo)類,而CGlib是通過(guò)繼承目標(biāo)類,因而不能代理final類。
代理類在程序運(yùn)行前不存在、運(yùn)行時(shí)由程序動(dòng)態(tài)生成的代理方式稱為動(dòng)態(tài)代理。
Java 提供了動(dòng)態(tài)代理的實(shí)現(xiàn)方式,可以在運(yùn)行時(shí)刻動(dòng)態(tài)生成代理類。這種代理方式的一大好處是可以方便對(duì)代理類的函數(shù)做統(tǒng)一或特殊處理,如記錄所有函數(shù)執(zhí)行時(shí)間、所有函數(shù)執(zhí)行前添加驗(yàn)證判斷、對(duì)某個(gè)特殊函數(shù)進(jìn)行特殊操作,而不用像靜態(tài)代理方式那樣需要修改每個(gè)函數(shù)。
代理模式原理
代理模式就是給某一個(gè)對(duì)象創(chuàng)建了一個(gè)代理對(duì)象,由這個(gè)代理對(duì)象控制原對(duì)象的引用,而創(chuàng)建這個(gè)代理對(duì)象后可以在調(diào)用原對(duì)象時(shí)增加一些額外的操作。如下圖所示是代理模式的結(jié)構(gòu)。
接口:它是代理對(duì)象的真實(shí)對(duì)象要實(shí)現(xiàn)的接口
代理類:除了實(shí)現(xiàn)上述的接口外,還需要持有代理對(duì)象的引用
實(shí)現(xiàn)類:被代理的類,是目標(biāo)對(duì)象

創(chuàng)建代理對(duì)象的時(shí)序圖如下所示

動(dòng)態(tài)代理實(shí)現(xiàn)
JDK實(shí)現(xiàn)動(dòng)態(tài)代理包括三步:
- 新建委托類;
- 實(shí)現(xiàn)InvocationHandler接口,這是負(fù)責(zé)連接代理類和委托類的中間類必須實(shí)現(xiàn)的接口;
- 通過(guò)Proxy類新建代理類對(duì)象。
接口
public interface Greeting {
void sayHello(String name);
}
目標(biāo)類
public class GreetingImpl implements Greeting {
public void sayHello(String name) {
System.out.println("say Hello:"+name);
}
}
JDK代理類
InvocationHandler接口,前者Invocation為執(zhí)行,后者Handler為句柄(實(shí)際對(duì)象的間接引用)。對(duì)于invoke函數(shù),調(diào)用代理對(duì)象的所有方法都會(huì)調(diào)用這個(gè)函數(shù)。
public class JDKDynamicProxy implements InvocationHandler {
private Object target;
public JDKDynamicProxy(Object target){
this.target = target;
}
public <T> T getPoxy(){
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target,args);
after();
return result;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}
cglib代理類
public class CGLibDynamicProxy implements MethodInterceptor {
private static CGLibDynamicProxy instance = new CGLibDynamicProxy();
private CGLibDynamicProxy(){
}
public static CGLibDynamicProxy getInstance(){
return instance;
}
public <T> T getProxy(Class<T> cls){
return (T) Enhancer.create(cls,this);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target,args);
after();
return result;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}
測(cè)試
public class Client {
public static void main(String[] args) {
//JDK動(dòng)態(tài)代理
Greeting greeting = new JDKDynamicProxy(new GreetingImpl()).getPoxy();
greeting.sayHello("Jack");
//CGLib動(dòng)態(tài)代理
Greeting greeting2 = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
greeting2.sayHello("Rose");
}
}
結(jié)果
before
say Hello:Jack
after
before
say Hello:Rose
after
參考資料
《架構(gòu)探險(xiǎn)》
《深入分析Java Web技術(shù)內(nèi)幕》