- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > 編程語(yǔ)言 >
- Java通俗易懂系列設計模式之代理模式
國內程序員好像普遍對百度都沒(méi)好感,而且百度近些年產(chǎn)生了不少負面的新聞,像16年的魏則西事件,近期的導演吳京黑白照事件,以及最近作家六六斥百度李彥宏:“你是做搜索引擎還是騙子首領(lǐng)”,還有一件就是與程序員有關(guān)的:搜索Julia語(yǔ)言,在百度和Google得出首條搜索結果的差異性而被吐槽。Google雖然受歡迎,但是在國內因內容審查問(wèn)題未解決而不能使用,如果我們要使用它就必須使用代理服務(wù)器,由于放置代理服務(wù)器的地區區域可以訪(fǎng)問(wèn)google,所以我們可以先訪(fǎng)問(wèn)代理服務(wù)器,通過(guò)代理服務(wù)器轉發(fā)我們的請求。這是現實(shí)生活中的一種代理模式的實(shí)例,當然現實(shí)生活中這種實(shí)例很不少,像明星都有助理,打官司有代理律師等等,這種思想也可以用到我們程序設計中。
在設計模式中代理模式可以分為靜態(tài)代理和動(dòng)態(tài)代理,而動(dòng)態(tài)代理根據代理的對象類(lèi)型不同又可以分為Jdk動(dòng)態(tài)代理和Cglib動(dòng)態(tài)代理。
意圖:為其他對象提供一種代理以控制對這個(gè)對象的訪(fǎng)問(wèn)。
主要解決:在直接訪(fǎng)問(wèn)對象時(shí)帶來(lái)的問(wèn)題,比如說(shuō):要訪(fǎng)問(wèn)的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創(chuàng )建開(kāi)銷(xiāo)很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪(fǎng)問(wèn)),直接訪(fǎng)問(wèn)會(huì )給使用者或者系統結構帶來(lái)很多麻煩,我們可以在訪(fǎng)問(wèn)此對象時(shí)加上一個(gè)對此對象的訪(fǎng)問(wèn)層。
何時(shí)使用:想在訪(fǎng)問(wèn)一個(gè)類(lèi)時(shí)做一些控制。
如何解決:增加中間層。
關(guān)鍵代碼:實(shí)現與被代理類(lèi)組合。
近幾年中國電影行業(yè)蓬勃發(fā)展,電影攝制需要的一種特殊演員->替身,主要任務(wù)是代替影片中原演員表演某些特殊的、高難度的動(dòng)作和技能或原演員所不能勝任的驚險動(dòng)作,如武打、騎術(shù)、駕車(chē)等。拍攝的時(shí)候雖然是替身在拍攝,但是呈現在熒幕前我們觀(guān)眾卻不知道是替身而認為是明星的真實(shí)拍攝,代理模式也有這種特點(diǎn),雖然是代理類(lèi)在完成任務(wù),但是呈現出來(lái)的卻是真實(shí)類(lèi)的實(shí)現。接下來(lái)我們以這種生活中的實(shí)例來(lái)作示例:
公共表演接口的定義
/** 表演 */ public interface Performance { void act(); }
明星的實(shí)體類(lèi)
/** 明星 */ public class Actor implements Performance { @Override public void act() { System.out.println("明星上場(chǎng)拍功夫電影"); } }
替身演員的實(shí)體類(lèi)
/** * 替身演員 */ public class Stuntman implements Performance { private Actor actor; @Override public void act() { if (actor == null) { actor = new Actor(); } System.out.println("替身演員表演跳火車(chē)."); actor.act(); System.out.println("替身演員表演空中360°旋轉飛踢."); } }
執行Demo
public class ProxyPatternDemo { public static void main(String[] args) { System.out.println("------電影拍攝開(kāi)始------"); Performance perform = new Stuntman(); perform.act(); System.out.println("------電影拍攝結束------"); } }
執行程序,輸出結果:
------電影拍攝開(kāi)始------
替身演員表演跳火車(chē).
明星上場(chǎng)拍功夫電影
替身演員表演空中360°旋轉飛踢.
1、Jdk動(dòng)態(tài)代理是由Java內部的反射機制來(lái)實(shí)現的,目標類(lèi)基于統一的接口InvocationHandler。
2、代理對象是在程序運行時(shí)產(chǎn)生的,而不是編譯期;
3、對代理對象的所有接口方法調用都會(huì )轉發(fā)到InvocationHandler.invoke()方法,在invoke()方法里我們可以加入任何邏輯,比如修改方法參數,加入日志功能、安全檢查功能等;之后我們通過(guò)某種方式執行真正的方法體,
4、對于從Object中繼承的方法,JDK動(dòng)態(tài)代理會(huì )把hashCode()、equals()、toString()這三個(gè)非接口方法轉發(fā)給InvocationHandler,其余的Object方法則不會(huì )轉發(fā)。詳見(jiàn)JDK Proxy官方文檔。
jdk動(dòng)態(tài)代理實(shí)現
public class JdkDynamicProxy implements InvocationHandler { private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("替身演員表演跳火車(chē)."); Object o = method.invoke(target, args); System.out.println("替身演員表演空中360°旋轉飛踢."); return o; } public Object bind(Object target) { //取得代理對象 this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
執行Demo
public static void main(String[] args) { //創(chuàng )建JDK動(dòng)態(tài)代理類(lèi) JdkDynamicProxy proxy = new JdkDynamicProxy(); //綁定對象 Performance performProxy = (Performance) proxy.bind(new Actor()); System.out.println("------電影拍攝開(kāi)始------"); performProxy.act(); System.out.println("------電影拍攝結束------"); }
執行結果
------電影拍攝開(kāi)始------
替身演員表演跳火車(chē).
明星上場(chǎng)拍功夫電影
替身演員表演空中360°旋轉飛踢.
Java動(dòng)態(tài)代理為我們提供了非常靈活的代理機制,但Jdk動(dòng)態(tài)代理是基于接口的,如果對象沒(méi)有實(shí)現接口我們該如何代理呢?答案是Cglib動(dòng)態(tài)代理。
cglib動(dòng)態(tài)代理底層則是借助asm來(lái)實(shí)現的,它允許我們在運行時(shí)對字節碼進(jìn)行修改和動(dòng)態(tài)生成,cglib這種第三方類(lèi)庫實(shí)現的動(dòng)態(tài)代理應用更加廣泛,且在效率上更有優(yōu)勢。
目標類(lèi)基于統一的接口MethodInterceptor。
CGLIB的核心類(lèi):
net.sf.cglib.proxy.Enhancer – 主要的增強類(lèi)。
net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類(lèi),它是Callback接口的子接口,需要用戶(hù)實(shí)現。
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類(lèi)的代理類(lèi),可以方便的實(shí)現對源對象方法的調用。
我們要使用cglib代理必須引入cglib的jar包(package net.sf.cglib.proxy;),我在這里使用的是spring包中cglib,其實(shí)和單獨的引cglib包是一樣的,只不過(guò)spring為了版本不沖突,將cglib包含在自己的包中。
cglib動(dòng)態(tài)代理實(shí)現:
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibDynamicProxy implements MethodInterceptor { private Object target; //創(chuàng )建代理對象 public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回調方法 enhancer.setCallback(this); // 創(chuàng )建代理對象 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("替身演員表演跳火車(chē)."); Object result = methodProxy.invokeSuper(o, objects); System.out.println("替身演員表演空中360°旋轉飛踢."); return result; } }
執行Demo
public static void main(String[] args) { CglibDynamicProxy cglibProxy = new CglibDynamicProxy(); Performance userService = (Performance) cglibProxy.getInstance(new Actor()); System.out.println("------電影拍攝開(kāi)始------"); userService.act(); System.out.println("------電影拍攝結束------"); }
執行結果
------電影拍攝開(kāi)始------
替身演員表演跳火車(chē).
明星上場(chǎng)拍功夫電影
替身演員表演空中360°旋轉飛踢.
1、通過(guò)以上的例子我們可以發(fā)現代理模式的特點(diǎn):
優(yōu)點(diǎn):
缺點(diǎn):
2、Jdk動(dòng)態(tài)代理和Cglib動(dòng)態(tài)代理的區別:
以上就是Java通俗易懂系列設計模式之代理模式的詳細內容,更多關(guān)于Java設計模式的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站