Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的設(shè)計(jì)模式之一。代理模式的主要作用是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。
代理概念?:為某個(gè)對(duì)象提供一個(gè)代理,以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 代理類和委托類有共同的父類或父接口,這樣在任何使用委托類對(duì)象的地方都可以用代理對(duì)象替代。代理類負(fù)責(zé)請(qǐng)求的預(yù)處理、過(guò)濾、將請(qǐng)求分派給委托類處理、以及委托類執(zhí)行完請(qǐng)求后的后續(xù)處理。
?
下面以明星為例模擬需求說(shuō)明靜態(tài)代理和動(dòng)態(tài)代理。
一、首先看靜態(tài)代理
看下圖:歌迷希望明星許巍唱歌(許巍即是目標(biāo)對(duì)象),但不可能直接找到許巍,只能通過(guò)許巍經(jīng)紀(jì)人,然后經(jīng)紀(jì)人讓許巍唱歌。這里的經(jīng)紀(jì)人即是許巍的一個(gè)代理對(duì)象,這樣就可以阻止對(duì)目標(biāo)對(duì)象的直接訪問(wèn)。
代理接口
1 package com.lizhou.test.proxy; 2 3 /** 4 * 代理接口:明星 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public interface Star { 9 10 /** 11 * 明星唱歌 12 */ 13 void sing(String song); 14 15 }委托類:真正執(zhí)行任務(wù)的類
1 package com.lizhou.test.proxy; 2 3 /** 4 * 委托類:真正執(zhí)行任務(wù)的類(許巍),實(shí)現(xiàn)了代理接口 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public class Xuwei implements Star { 9 10 public void sing(String song) { 11 System.out.println("許巍唱歌:"+song); 12 } 13 14 }靜態(tài)代理類,實(shí)現(xiàn)了代理接口:許巍經(jīng)紀(jì)人
1 package com.lizhou.test.proxy; 2 3 /** 4 * 靜態(tài)代理類,實(shí)現(xiàn)了代理接口:許巍經(jīng)紀(jì)人 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public class XuweiProxy implements Star { 9 10 /** 11 * 代理類持有一個(gè)委托類的對(duì)象引用 12 */ 13 private Star star; 14 15 public XuweiProxy(Star star){ 16 this.star = star; 17 } 18 19 /** 20 * 代理類負(fù)責(zé)請(qǐng)求的預(yù)處理、過(guò)濾、將請(qǐng)求分派給委托類處理、以及委托類執(zhí)行完請(qǐng)求后的后續(xù)處理 21 */ 22 public void sing(String song) { 23 //代理類負(fù)責(zé)請(qǐng)求的預(yù)處理 24 System.out.println("-----預(yù)處理:分析打電話給許巍經(jīng)紀(jì)人要許巍唱歌-----"); 25 System.out.println("-----預(yù)處理:經(jīng)紀(jì)人要求許巍唱歌-----"); 26 27 //將請(qǐng)求分派給委托類處理 28 star.sing(song); 29 30 //委托類執(zhí)行完請(qǐng)求后的后續(xù)處理 31 System.out.println("-----后處理:許巍唱歌完畢-----"); 32 } 33 34 }粉絲:通過(guò)代理對(duì)象讓目標(biāo)對(duì)象執(zhí)行任務(wù)
1 package com.lizhou.test.proxy; 2 3 /** 4 * 粉絲呼吁許巍唱歌 5 * 粉絲不可能直接找許巍唱歌,需要先聯(lián)系經(jīng)紀(jì)人,經(jīng)紀(jì)人再讓許巍唱歌 6 * @author bojiangzhou 7 * @date 2016年5月5日 8 */ 9 public class Fans { 10 11 public static void main(String[] args) { 12 //創(chuàng)建代理對(duì)象(即經(jīng)紀(jì)人) 13 XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei()); 14 15 xuweiProxy.sing("完美生活"); 16 } 17 18 }?
這就是一個(gè)靜態(tài)代理。所謂靜態(tài)也就是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運(yùn)行前就確定了。?
靜態(tài)代理的缺點(diǎn):?
1)代理對(duì)象的一個(gè)接口只服務(wù)于一種類型的對(duì)象,如果要代理的方法很多,勢(shì)必要為每一種方法都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時(shí)就無(wú)法勝任了。?
2)如果接口增加一個(gè)方法,除了所有實(shí)現(xiàn)類需要實(shí)現(xiàn)這個(gè)方法外,所有代理類也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度。?
--------------------------------------------------------------------------------------------------------------------------------------------
二、動(dòng)態(tài)代理
看下圖:動(dòng)態(tài)代理時(shí),代理類(即經(jīng)紀(jì)人)是沒(méi)有實(shí)現(xiàn)代理接口的(Star),是一個(gè)獨(dú)立的類。那代理類如何代理目標(biāo)對(duì)象呢?此時(shí)代理類則借助Proxy在運(yùn)行時(shí)動(dòng)態(tài)指向目標(biāo)對(duì)象。
?Java提供了一個(gè)Proxy類,Proxy?提供用于創(chuàng)建動(dòng)態(tài)代理類和實(shí)例的靜態(tài)方法,它還是由這些方法創(chuàng)建的所有動(dòng)態(tài)代理類的超類。
Proxy類:
---------------------
還是以明星為例模擬需求動(dòng)態(tài)代理
?代理接口不變:
1 package com.lizhou.test.proxy; 2 3 /** 4 * 代理接口:明星 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public interface Star { 9 10 /** 11 * 明星唱歌 12 */ 13 void sing(String song); 14 15 }目標(biāo)對(duì)象不變:
1 package com.lizhou.test.proxy; 2 3 /** 4 * 委托類:真正執(zhí)行任務(wù)的類(許巍),實(shí)現(xiàn)了代理接口 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public class Xuwei implements Star { 9 10 public void sing(String song) { 11 System.out.println("許巍唱歌:"+song); 12 } 13 14 }代理類:通過(guò)Proxy來(lái)動(dòng)態(tài)產(chǎn)生目標(biāo)對(duì)象
動(dòng)態(tài)代理類開(kāi)發(fā)步驟:
1)寫(xiě)一個(gè)普通類,無(wú)需任何繼承或?qū)崿F(xiàn)
2)寫(xiě)一個(gè)實(shí)例變量,記住代理誰(shuí),即目標(biāo)對(duì)象
3)使用構(gòu)造方法為實(shí)例變量賦值
4)寫(xiě)一個(gè)普通方法,該方法的返回值是接口,該接口是目標(biāo)對(duì)象的實(shí)現(xiàn)接口
粉絲類:
1 package com.lizhou.test.proxy; 2 3 /** 4 * 粉絲呼吁許巍唱歌 5 * 粉絲不可能直接找許巍唱歌,需要先聯(lián)系經(jīng)紀(jì)人,經(jīng)紀(jì)人再讓許巍唱歌 6 * @author bojiangzhou 7 * @date 2016年5月5日 8 */ 9 public class Fans { 10 11 public static void main(String[] args) { 12 //創(chuàng)建代理對(duì)象(即經(jīng)紀(jì)人) 13 XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei()); 14 //通過(guò)代理對(duì)象找目標(biāo)對(duì)象 15 Star star = xuweiProxy.getProxy(); 16 star.sing("完美生活"); 17 } 18 19 }結(jié)果:可以看得出來(lái),雖然調(diào)用的是star.sing(),但并不是直接調(diào)用的。因?yàn)榇颂巗tar是一個(gè)代理對(duì)象,先調(diào)用的是代理對(duì)象的invoke方法(invoke表示動(dòng)態(tài)代理對(duì)象的攔截方法,每次調(diào)用目標(biāo)對(duì)象都會(huì)執(zhí)行該invoke()),我們?cè)诶锩婵梢宰鲆恍╊A(yù)處理及后處理等操作。
?動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成,所以不存在代理類的字節(jié)碼文件。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定。
動(dòng)態(tài)代理優(yōu)點(diǎn):?動(dòng)態(tài)代理與靜態(tài)代理相比較,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個(gè)集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數(shù)量比較多的時(shí)候,我們可以進(jìn)行靈活處理,而不需要像靜態(tài)代理那樣每一個(gè)方法進(jìn)行中轉(zhuǎn)。
JDK動(dòng)態(tài)代理總結(jié):
1,JAVA動(dòng)態(tài)代理是使用java.lang.reflect包中的Proxy類與InvocationHandler接口這兩個(gè)來(lái)完成的。
2,要使用JDK動(dòng)態(tài)代理,必須要定義接口。
3,JDK動(dòng)態(tài)代理將會(huì)攔截所有pubic的方法(因?yàn)橹荒苷{(diào)用接口中定義的方法),這樣即使在接口中增加了新的方法,不用修改代碼也會(huì)被攔截。
4,如果只想攔截一部分方法,可以在invoke方法中對(duì)要執(zhí)行的方法名進(jìn)行判斷。
?--------------------------------------------------------------------------------------------------------------------------------------------
三、列舉一個(gè)動(dòng)態(tài)代理的實(shí)際應(yīng)用例子:解決網(wǎng)站POST和GET的統(tǒng)一編碼問(wèn)題
思路:使用過(guò)濾器攔截請(qǐng)求,自定義一個(gè)代理request的類,然后將代理對(duì)象返回
看代碼:
1 package com.lizhou.test.proxy; 2 3 import java.io.IOException; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 8 import javax.servlet.Filter; 9 import javax.servlet.FilterChain; 10 import javax.servlet.FilterConfig; 11 import javax.servlet.ServletException; 12 import javax.servlet.ServletRequest; 13 import javax.servlet.ServletResponse; 14 import javax.servlet.http.HttpServletRequest; 15 16 /** 17 * 使用代理類解決網(wǎng)站POST和GET的統(tǒng)一編碼問(wèn)題 18 * @author bojiangzhou 19 * @date 2016年5月5日 20 */ 21 public class CharacterEncodingFilter implements Filter { 22 23 public void destroy() { 24 25 } 26 27 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 28 //獲取request的代理對(duì)象,這里request的代理對(duì)象對(duì)字符編碼進(jìn)行了處理的 29 RequestProxy proxy = new RequestProxy((HttpServletRequest) request); 30 //然后將代理對(duì)象傳過(guò)去即可 31 chain.doFilter(proxy.getProxy(), response); 32 } 33 34 public void init(FilterConfig fConfig) throws ServletException { 35 36 } 37 38 } 39 40 /** 41 * request代理類 42 * @author bojiangzhou 43 * @date 2016年5月5日 44 */ 45 class RequestProxy { 46 //明確代理對(duì)象 47 private HttpServletRequest request; 48 49 public RequestProxy(HttpServletRequest request){ 50 this.request = request; 51 } 52 53 /** 54 * 返回request的代理對(duì)象 55 * @return 56 */ 57 public ServletRequest getProxy(){ 58 59 return (ServletRequest) Proxy.newProxyInstance( 60 RequestProxy.class.getClassLoader(), 61 request.getClass().getInterfaces(), 62 new InvocationHandler() { 63 64 public Object invoke( 65 Object proxy, 66 Method method, 67 Object[] args) throws Throwable { 68 System.out.println("執(zhí)行代理對(duì)象方法"); 69 if("getParameter".equals(method.getName())){ 70 //如果調(diào)用了request的getParameter方法,則對(duì)其進(jìn)行編碼處理 71 72 String param = (String) args[0]; 73 if("get".equalsIgnoreCase(request.getMethod())){ 74 //如果是get請(qǐng)求方式 75 String value = request.getParameter(param); 76 return new String(value.getBytes("ISO8859-1"), "UTF-8"); 77 } else{ 78 //post方式 79 request.setCharacterEncoding("UTF-8"); 80 return request.getParameter(param); 81 } 82 } else{ 83 //放行資源 84 return method.invoke(request, args); 85 } 86 } 87 }); 88 } 89 90 }Servlet類:
1 package com.lizhou.test.proxy; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.annotation.WebServlet; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class LoginServlet extends HttpServlet { 11 12 private static final long serialVersionUID = 1L; 13 14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 //注意此處的request實(shí)際上是一個(gè)代理對(duì)象,在調(diào)用getParmeter方法時(shí),實(shí)際調(diào)用的是代理對(duì)象的invoke方法。 16 String name = request.getParameter("name"); 17 System.out.println("doGet 姓名:"+name); 18 } 19 20 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 21 this.doGet(request, response); 22 } 23 24 }?
個(gè)人理解:代理的作用其實(shí)就類似于過(guò)濾器、Struts2的攔截器、Spring的切面/通知,在進(jìn)行某個(gè)請(qǐng)求的時(shí)候,攔截該請(qǐng)求,做一些預(yù)處理、過(guò)濾、后置處理等,代理最重要的就是讓你不能直接訪問(wèn)到實(shí)際的目標(biāo)對(duì)象,使用的是目標(biāo)對(duì)象的一個(gè)代理對(duì)象。
?
?本文部分參考:java靜態(tài)代理和動(dòng)態(tài)代理
?
本文純屬個(gè)人學(xué)習(xí)筆記,因?yàn)榇肀容^少用到,所以這次再次學(xué)習(xí)后做個(gè)筆記以便以后需要的時(shí)候快速學(xué)習(xí)。
如有不當(dāng)之處,敬請(qǐng)指出O(∩_∩)O~
?
轉(zhuǎn)載于:https://www.cnblogs.com/chiangchou/p/java-proxy.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Java代理模式/静态代理/动态代理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 内核模块可选信息
- 下一篇: java美元兑换,(Java实现) 美元