java代理模式-登場(chǎng)
什么是代理模式多律?
代理模式是java中的一種設(shè)計(jì)模式南吮,它其實(shí)就是設(shè)置一個(gè)中間環(huán)節(jié)來代理你要對(duì)原目標(biāo)對(duì)象的訪問龄句。簡(jiǎn)言之傀蚌,代理模式就是有一個(gè)充當(dāng)代理者
身份的類或方法來控制原對(duì)象的引用勋桶。
還是不太理解脱衙,你能舉個(gè)例子說明一下嗎?
這里一個(gè)很好的例子(引用鏈接): 一個(gè)公司是賣攝像頭的例驹,但公司不直接跟用戶打交道捐韩,而是通過代理商跟用戶打交道。如果:公司接口中有一個(gè)賣產(chǎn)品的方法眠饮,那么公司需要實(shí)現(xiàn)這個(gè)方法奥帘,而代理商也必須實(shí)現(xiàn)這個(gè)方法。如果公司賣多少錢仪召,代理商也賣多少錢寨蹋,那么代理商就賺不了錢。所以代理商在調(diào)用公司的賣方法后扔茅,加上自己的利潤(rùn)然后再把產(chǎn)品賣給客戶已旧。而客戶不直接跟公司打交道,或者客戶根本不知道公司的存在召娜,然而客戶最終卻買到了產(chǎn)品运褪。*
它用途應(yīng)該挺大的吧?
它是java中常用的設(shè)計(jì)模式之一玖瘸,并且在spring框架中有廣泛應(yīng)用(AOP)秸讹,它一般有三種模式:靜態(tài)代理、jdk1.6+中的動(dòng)態(tài)代理雅倒、cglib動(dòng)態(tài)代理璃诀。
java代理模式-靜態(tài)代理
首先,動(dòng)態(tài)代理是通過反射機(jī)制來處理相關(guān)業(yè)務(wù)方法蔑匣。那么你也猜到了劣欢,靜態(tài)代理則是通過編寫代理類來完成代理過程棕诵。
公司要賣的產(chǎn)品(實(shí)現(xiàn)接口)
package top.code666.porxy;
//公司中的業(yè)務(wù)接口
public interface ICompany {
//賣攝像頭
void sellCamera();
}
公司的實(shí)現(xiàn)類(目標(biāo)類)
package top.code666.porxy;
//公司類
public class CompanyImpl implements ICompany{
@Override
public void sellCamera() {
System.out.println("一個(gè)很好的Camera,高清無碼凿将。(xx公司生產(chǎn)Camera并出售給代理商校套,定價(jià)¥1000)");
}
}
代理商的實(shí)現(xiàn)類(代理類)
package top.code666.porxy;
//代理類
public class ProxyImpl implements ICompany{
private CompanyImpl ci;
public ProxyImpl(CompanyImpl ci){
this.ci = ci;
}
@Override
public void sellCamera() {
System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了牧抵!不要998笛匙,不要98,只要9998你就可以把它帶回家~");
ci.sellCamera();
System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功灭忠,三分鐘內(nèi)無條件退換哦~");
}
}
客戶(測(cè)試類)
package top.code666.porxy;
//一個(gè)有錢的人
public class RichMan {
public static void main(String[] args) {
CompanyImpl ci = new CompanyImpl();
ProxyImpl pi = new ProxyImpl(ci);
pi.sellCamera();
}
}
運(yùn)行結(jié)果
(賣前打了一波廣告)ss微商成為xx公司的最大代理商膳算,現(xiàn)在正式售賣Camera了!不要998弛作,不要98涕蜂,只要9998你就可以把它帶回家~
一個(gè)很好的Camera,高清無碼映琳。(xx公司生產(chǎn)Camera并出售給代理商机隙,定價(jià)¥1000)
(賣后服務(wù))恭喜你購(gòu)買成功,三分鐘內(nèi)無條件退換哦~
你現(xiàn)在也感覺到了萨西,靜態(tài)代理就是這么的簡(jiǎn)單有鹿。不過它卻存在許多弊端:
- 如果要代理的產(chǎn)品多了,那么你的整個(gè)項(xiàng)目將變得非常冗余谎脯。
- 如果要修改接口葱跋,那么你的目標(biāo)對(duì)象和代理對(duì)象都要修改,所以不易于維護(hù)源梭。
這時(shí)我們的動(dòng)態(tài)代理就可以很好的解決這些弊端了娱俺。燈登瞪等~ ,讓我們歡迎動(dòng)態(tài)代理閃亮登場(chǎng)吧(前方高能废麻,如果你對(duì)java的反射機(jī)制不是很了解荠卷,請(qǐng)先去學(xué)習(xí)反射相關(guān)知識(shí)后再過來)
java代理模式-動(dòng)態(tài)代理(JDK1.6+)
JDK1.6以上的版本自帶了動(dòng)態(tài)代理方法。我們只需要實(shí)現(xiàn)InvocationHandler就可以使用代理了烛愧。前提:你的目標(biāo)對(duì)象必須實(shí)現(xiàn)接口油宜,否則不能使用JDK動(dòng)態(tài)代理
接口與目標(biāo)類是與上一樣,所以這里不再重復(fù)
代理商的實(shí)現(xiàn)類(代理類)
package top.code666.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理對(duì)象
public class ProxyImpl implements InvocationHandler{
//目標(biāo)對(duì)象
private Object target;
public ProxyImpl(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商怜姿,現(xiàn)在正式售賣Camera了慎冤!不要998,不要98沧卢,只要9998你就可以把它帶回家~");
Object result = method.invoke(target, args); // 調(diào)用目標(biāo)類的方法
System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功粪薛,三分鐘內(nèi)無條件退換哦~");
return result;
}
}
客戶(測(cè)試類)
package top.code666.jdkproxy;
import java.lang.reflect.Proxy;
//一個(gè)有錢的人
public class RichMan {
public static void main(String[] args) {
ICompany ic = new CompanyImpl();
ProxyImpl pi = new ProxyImpl(ic); //傳入目標(biāo)對(duì)象
ICompany proxySubject = (ICompany) Proxy.newProxyInstance(
CompanyImpl.class.getClassLoader(),
CompanyImpl.class.getInterfaces(), pi); // new代理實(shí)例
proxySubject.sellCamera();
}
}
可以發(fā)現(xiàn),在JDK的動(dòng)態(tài)代理中搏恤,我們的代理類是不需要再重復(fù)寫了的违寿,可以共用同一個(gè)。但是它需要定義接口熟空,然后才能實(shí)現(xiàn)代理功能藤巢,所以還是存在一定的局限性。下面讓我們來看看CGLIB是怎么實(shí)現(xiàn)代理功能的吧息罗,它會(huì)不會(huì)不需要就能實(shí)現(xiàn)呢掂咒?
java代理模式-動(dòng)態(tài)代理(cglib.jar)
cglib可以在目標(biāo)類不實(shí)現(xiàn)接口方法時(shí),也能夠給這樣的類提供動(dòng)態(tài)代理迈喉。而JDK中是不行的绍刮。Spring在給某個(gè)類提供動(dòng)態(tài)代理時(shí)會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理中動(dòng)態(tài)的選擇。(其實(shí)JDK中的動(dòng)態(tài)代理是要比CGLIB中動(dòng)態(tài)代理效率要稍微高一點(diǎn)點(diǎn)的)
使用cglib需要導(dǎo)入cglib.jar+asm.jar
目標(biāo)類(因?yàn)槭侵苯訌?fù)制的挨摸,類名啥的我就沒改了孩革,Impl或許對(duì)一些人來說有點(diǎn)礙眼……)
package top.code666.cglib;
//公司類
public class CompanyImpl{
public void sellCamera() {
System.out.println("一個(gè)很好的Camera毙芜,高清無碼钢拧。(xx公司生產(chǎn)Camera并出售給代理商淑际,定價(jià)¥1000)");
}
}
代理類
package top.code666.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//代理對(duì)象
public class ProxyImpl implements MethodInterceptor{
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商探遵,現(xiàn)在正式售賣Camera了奥邮!不要998钙勃,不要98叭披,只要9998你就可以把它帶回家~");
Object result = arg3.invokeSuper(arg0, arg2);
System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功颅筋,三分鐘內(nèi)無條件退換哦~");
return result;
}
}
測(cè)試類
package top.code666.cglib;
import net.sf.cglib.proxy.Enhancer;
//一個(gè)有錢的人
public class RichMan {
public static void main(String ... args) {
CompanyImpl target = new CompanyImpl();
RichMan test = new RichMan();
CompanyImpl proxyTarget = (CompanyImpl) test.createProxy(CompanyImpl.class);
proxyTarget.sellCamera();
}
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new ProxyImpl());
return enhancer.create();
}
}
JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)置逻。
代理對(duì)象的生成過程由Enhancer類實(shí)現(xiàn)推沸,大概步驟如下:
- 生成代理類Class的二進(jìn)制字節(jié)碼;
- 通過Class.forName加載二進(jìn)制字節(jié)碼券坞,生成Class對(duì)象鬓催;
- 通過反射機(jī)制獲取實(shí)例構(gòu)造,并初始化代理類對(duì)象报慕。
總結(jié)
- 靜態(tài)代理實(shí)現(xiàn)較簡(jiǎn)單深浮,只要代理對(duì)象對(duì)目標(biāo)對(duì)象進(jìn)行包裝,即可實(shí)現(xiàn)增強(qiáng)功能眠冈,但靜態(tài)代理只能為一個(gè)目標(biāo)對(duì)象服務(wù)飞苇,如果目標(biāo)對(duì)象過多,則會(huì)產(chǎn)生很多代理類蜗顽。
- JDK動(dòng)態(tài)代理需要目標(biāo)對(duì)象實(shí)現(xiàn)業(yè)務(wù)接口布卡,代理類只需實(shí)現(xiàn)InvocationHandler接口。
- 動(dòng)態(tài)代理生成的類為 class com.sun.proxy.$Proxy4雇盖,cglib代理生成的類為class
com.cglib.UserDao$$EnhancerByCGLIB$$552188b6忿等。
- 靜態(tài)代理在編譯時(shí)產(chǎn)生class字節(jié)碼文件,可以直接使用崔挖,效率高贸街。
- 動(dòng)態(tài)代理必須實(shí)現(xiàn)InvocationHandler接口庵寞,通過反射代理方法,比較消耗系統(tǒng)性能薛匪,但可以減少代理類的數(shù)量捐川,使用更靈活。
- cglib代理無需實(shí)現(xiàn)接口逸尖,通過生成類字節(jié)碼實(shí)現(xiàn)代理古沥,比反射稍快,不存在性能問題娇跟,但cglib會(huì)繼承目標(biāo)對(duì)象岩齿,需要重寫方法,所以目標(biāo)對(duì)象不能為final類苞俘。
覺得還行可以點(diǎn)個(gè)星星哦