java代理模式-登場
什么是代理模式?
代理模式是java中的一種設(shè)計模式,它其實就是設(shè)置一個中間環(huán)節(jié)來代理你要對原目標(biāo)對象的訪問。簡言之,代理模式就是有一個充當(dāng)代理者身份的類或方法來控制原對象的引用。
還是不太理解,你能舉個例子說明一下嗎?
這里一個很好的例子(引用鏈接): 一個公司是賣攝像頭的,但公司不直接跟用戶打交道,而是通過代理商跟用戶打交道。如果:公司接口中有一個賣產(chǎn)品的方法,那么公司需要實現(xiàn)這個方法,而代理商也必須實現(xiàn)這個方法。如果公司賣多少錢,代理商也賣多少錢,那么代理商就賺不了錢。所以代理商在調(diào)用公司的賣方法后,加上自己的利潤然后再把產(chǎn)品賣給客戶。而客戶不直接跟公司打交道,或者客戶根本不知道公司的存在,然而客戶最終卻買到了產(chǎn)品。*
它用途應(yīng)該挺大的吧?
它是java中常用的設(shè)計模式之一,并且在spring框架中有廣泛應(yīng)用(AOP),它一般有三種模式:靜態(tài)代理、jdk1.6+中的動態(tài)代理、cglib動態(tài)代理。
java代理模式-靜態(tài)代理
首先,動態(tài)代理是通過反射機制來處理相關(guān)業(yè)務(wù)方法。那么你也猜到了,靜態(tài)代理則是通過編寫代理類來完成代理過程。
公司要賣的產(chǎn)品(實現(xiàn)接口)
package top.code666.porxy;
//公司中的業(yè)務(wù)接口
public interface ICompany {
//賣攝像頭
void sellCamera();
}
公司的實現(xiàn)類(目標(biāo)類)
package top.code666.porxy;
//公司類
public class CompanyImpl implements ICompany{
@Override
public void sellCamera() {
System.out.println("一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)");
}
}
代理商的實現(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ù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
}
}
客戶(測試類)
package top.code666.porxy;
//一個有錢的人
public class RichMan {
public static void main(String[] args) {
CompanyImpl ci = new CompanyImpl();
ProxyImpl pi = new ProxyImpl(ci);
pi.sellCamera();
}
}
運行結(jié)果
(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了!不要998,不要98,只要9998你就可以把它帶回家~
一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)
(賣后服務(wù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~
你現(xiàn)在也感覺到了,靜態(tài)代理就是這么的簡單。不過它卻存在許多弊端:
- 如果要代理的產(chǎn)品多了,那么你的整個項目將變得非常冗余。
- 如果要修改接口,那么你的目標(biāo)對象和代理對象都要修改,所以不易于維護。
這時我們的動態(tài)代理就可以很好的解決這些弊端了。燈登瞪等~ ,讓我們歡迎動態(tài)代理閃亮登場吧(前方高能,如果你對java的反射機制不是很了解,請先去學(xué)習(xí)反射相關(guān)知識后再過來)
java代理模式-動態(tài)代理(JDK1.6+)
JDK1.6以上的版本自帶了動態(tài)代理方法。我們只需要實現(xiàn)InvocationHandler就可以使用代理了。前提:你的目標(biāo)對象必須實現(xiàn)接口,否則不能使用JDK動態(tài)代理
接口與目標(biāo)類是與上一樣,所以這里不再重復(fù)
代理商的實現(xiàn)類(代理類)
package top.code666.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理對象
public class ProxyImpl implements InvocationHandler{
//目標(biāo)對象
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ù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
return result;
}
}
客戶(測試類)
package top.code666.jdkproxy;
import java.lang.reflect.Proxy;
//一個有錢的人
public class RichMan {
public static void main(String[] args) {
ICompany ic = new CompanyImpl();
ProxyImpl pi = new ProxyImpl(ic); //傳入目標(biāo)對象
ICompany proxySubject = (ICompany) Proxy.newProxyInstance(
CompanyImpl.class.getClassLoader(),
CompanyImpl.class.getInterfaces(), pi); // new代理實例
proxySubject.sellCamera();
}
}
可以發(fā)現(xiàn),在JDK的動態(tài)代理中,我們的代理類是不需要再重復(fù)寫了的,可以共用同一個。但是它需要定義接口,然后才能實現(xiàn)代理功能,所以還是存在一定的局限性。下面讓我們來看看CGLIB是怎么實現(xiàn)代理功能的吧,它會不會不需要就能實現(xiàn)呢?
java代理模式-動態(tài)代理(cglib.jar)
cglib可以在目標(biāo)類不實現(xiàn)接口方法時,也能夠給這樣的類提供動態(tài)代理。而JDK中是不行的。Spring在給某個類提供動態(tài)代理時會自動在JDK動態(tài)代理和CGLIB動態(tài)代理中動態(tài)的選擇。(其實JDK中的動態(tài)代理是要比CGLIB中動態(tài)代理效率要稍微高一點點的)
使用cglib需要導(dǎo)入cglib.jar+asm.jar
目標(biāo)類(因為是直接復(fù)制的,類名啥的我就沒改了,Impl或許對一些人來說有點礙眼……)
package top.code666.cglib;
//公司類
public class CompanyImpl{
public void sellCamera() {
System.out.println("一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)");
}
}
代理類
package top.code666.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//代理對象
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ù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
return result;
}
}
測試類
package top.code666.cglib;
import net.sf.cglib.proxy.Enhancer;
//一個有錢的人
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動態(tài)代理與CGLib動態(tài)代理均是實現(xiàn)Spring AOP的基礎(chǔ)。
代理對象的生成過程由Enhancer類實現(xiàn),大概步驟如下:
- 生成代理類Class的二進制字節(jié)碼;
- 通過Class.forName加載二進制字節(jié)碼,生成Class對象;
- 通過反射機制獲取實例構(gòu)造,并初始化代理類對象。
總結(jié)
- 靜態(tài)代理實現(xiàn)較簡單,只要代理對象對目標(biāo)對象進行包裝,即可實現(xiàn)增強功能,但靜態(tài)代理只能為一個目標(biāo)對象服務(wù),如果目標(biāo)對象過多,則會產(chǎn)生很多代理類。
- JDK動態(tài)代理需要目標(biāo)對象實現(xiàn)業(yè)務(wù)接口,代理類只需實現(xiàn)InvocationHandler接口。
- 動態(tài)代理生成的類為 class com.sun.proxy.$Proxy4,cglib代理生成的類為class
com.cglib.UserDao$$EnhancerByCGLIB$$552188b6。 - 靜態(tài)代理在編譯時產(chǎn)生class字節(jié)碼文件,可以直接使用,效率高。
- 動態(tài)代理必須實現(xiàn)InvocationHandler接口,通過反射代理方法,比較消耗系統(tǒng)性能,但可以減少代理類的數(shù)量,使用更靈活。
- cglib代理無需實現(xiàn)接口,通過生成類字節(jié)碼實現(xiàn)代理,比反射稍快,不存在性能問題,但cglib會繼承目標(biāo)對象,需要重寫方法,所以目標(biāo)對象不能為final類。
覺得還行可以點個星星哦