Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的設計模式之一。代理模式的主要作用是為其他對象提供一種代理以控制對這個對象的訪問。
代理概念?:為某個對象提供一個代理,以控制對這個對象的訪問。 代理類和委托類有共同的父類或父接口,這樣在任何使用委托類對象的地方都可以用代理對象替代。代理類負責請求的預處理、過濾、將請求分派給委托類處理、以及委托類執行完請求后的后續處理。
?
下面以明星為例模擬需求說明靜態代理和動態代理。
一、首先看靜態代理
看下圖:歌迷希望明星許巍唱歌(許巍即是目標對象),但不可能直接找到許巍,只能通過許巍經紀人,然后經紀人讓許巍唱歌。這里的經紀人即是許巍的一個代理對象,這樣就可以阻止對目標對象的直接訪問。
代理接口
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 }委托類:真正執行任務的類
1 package com.lizhou.test.proxy; 2 3 /** 4 * 委托類:真正執行任務的類(許巍),實現了代理接口 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 }靜態代理類,實現了代理接口:許巍經紀人
1 package com.lizhou.test.proxy; 2 3 /** 4 * 靜態代理類,實現了代理接口:許巍經紀人 5 * @author bojiangzhou 6 * @date 2016年5月5日 7 */ 8 public class XuweiProxy implements Star { 9 10 /** 11 * 代理類持有一個委托類的對象引用 12 */ 13 private Star star; 14 15 public XuweiProxy(Star star){ 16 this.star = star; 17 } 18 19 /** 20 * 代理類負責請求的預處理、過濾、將請求分派給委托類處理、以及委托類執行完請求后的后續處理 21 */ 22 public void sing(String song) { 23 //代理類負責請求的預處理 24 System.out.println("-----預處理:分析打電話給許巍經紀人要許巍唱歌-----"); 25 System.out.println("-----預處理:經紀人要求許巍唱歌-----"); 26 27 //將請求分派給委托類處理 28 star.sing(song); 29 30 //委托類執行完請求后的后續處理 31 System.out.println("-----后處理:許巍唱歌完畢-----"); 32 } 33 34 }粉絲:通過代理對象讓目標對象執行任務
1 package com.lizhou.test.proxy; 2 3 /** 4 * 粉絲呼吁許巍唱歌 5 * 粉絲不可能直接找許巍唱歌,需要先聯系經紀人,經紀人再讓許巍唱歌 6 * @author bojiangzhou 7 * @date 2016年5月5日 8 */ 9 public class Fans { 10 11 public static void main(String[] args) { 12 //創建代理對象(即經紀人) 13 XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei()); 14 15 xuweiProxy.sing("完美生活"); 16 } 17 18 }?
這就是一個靜態代理。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。?
靜態代理的缺點:?
1)代理對象的一個接口只服務于一種類型的對象,如果要代理的方法很多,勢必要為每一種方法都進行代理,靜態代理在程序規模稍大時就無法勝任了。?
2)如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的復雜度。?
--------------------------------------------------------------------------------------------------------------------------------------------
二、動態代理
看下圖:動態代理時,代理類(即經紀人)是沒有實現代理接口的(Star),是一個獨立的類。那代理類如何代理目標對象呢?此時代理類則借助Proxy在運行時動態指向目標對象。
?Java提供了一個Proxy類,Proxy?提供用于創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
Proxy類:
---------------------
還是以明星為例模擬需求動態代理
?代理接口不變:
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 }目標對象不變:
1 package com.lizhou.test.proxy; 2 3 /** 4 * 委托類:真正執行任務的類(許巍),實現了代理接口 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 }代理類:通過Proxy來動態產生目標對象
動態代理類開發步驟:
1)寫一個普通類,無需任何繼承或實現
2)寫一個實例變量,記住代理誰,即目標對象
3)使用構造方法為實例變量賦值
4)寫一個普通方法,該方法的返回值是接口,該接口是目標對象的實現接口
粉絲類:
1 package com.lizhou.test.proxy; 2 3 /** 4 * 粉絲呼吁許巍唱歌 5 * 粉絲不可能直接找許巍唱歌,需要先聯系經紀人,經紀人再讓許巍唱歌 6 * @author bojiangzhou 7 * @date 2016年5月5日 8 */ 9 public class Fans { 10 11 public static void main(String[] args) { 12 //創建代理對象(即經紀人) 13 XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei()); 14 //通過代理對象找目標對象 15 Star star = xuweiProxy.getProxy(); 16 star.sing("完美生活"); 17 } 18 19 }結果:可以看得出來,雖然調用的是star.sing(),但并不是直接調用的。因為此處star是一個代理對象,先調用的是代理對象的invoke方法(invoke表示動態代理對象的攔截方法,每次調用目標對象都會執行該invoke()),我們在里面可以做一些預處理及后處理等操作。
?動態代理類的源碼是在程序運行期間由JVM根據反射等機制動態的生成,所以不存在代理類的字節碼文件。代理類和委托類的關系是在程序運行時確定。
動態代理優點:?動態代理與靜態代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。
JDK動態代理總結:
1,JAVA動態代理是使用java.lang.reflect包中的Proxy類與InvocationHandler接口這兩個來完成的。
2,要使用JDK動態代理,必須要定義接口。
3,JDK動態代理將會攔截所有pubic的方法(因為只能調用接口中定義的方法),這樣即使在接口中增加了新的方法,不用修改代碼也會被攔截。
4,如果只想攔截一部分方法,可以在invoke方法中對要執行的方法名進行判斷。
?--------------------------------------------------------------------------------------------------------------------------------------------
三、列舉一個動態代理的實際應用例子:解決網站POST和GET的統一編碼問題
思路:使用過濾器攔截請求,自定義一個代理request的類,然后將代理對象返回
看代碼:
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 * 使用代理類解決網站POST和GET的統一編碼問題 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的代理對象,這里request的代理對象對字符編碼進行了處理的 29 RequestProxy proxy = new RequestProxy((HttpServletRequest) request); 30 //然后將代理對象傳過去即可 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 //明確代理對象 47 private HttpServletRequest request; 48 49 public RequestProxy(HttpServletRequest request){ 50 this.request = request; 51 } 52 53 /** 54 * 返回request的代理對象 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("執行代理對象方法"); 69 if("getParameter".equals(method.getName())){ 70 //如果調用了request的getParameter方法,則對其進行編碼處理 71 72 String param = (String) args[0]; 73 if("get".equalsIgnoreCase(request.getMethod())){ 74 //如果是get請求方式 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實際上是一個代理對象,在調用getParmeter方法時,實際調用的是代理對象的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 }?
個人理解:代理的作用其實就類似于過濾器、Struts2的攔截器、Spring的切面/通知,在進行某個請求的時候,攔截該請求,做一些預處理、過濾、后置處理等,代理最重要的就是讓你不能直接訪問到實際的目標對象,使用的是目標對象的一個代理對象。
?
?本文部分參考:java靜態代理和動態代理
?
本文純屬個人學習筆記,因為代理比較少用到,所以這次再次學習后做個筆記以便以后需要的時候快速學習。
如有不當之處,敬請指出O(∩_∩)O~
?
轉載于:https://www.cnblogs.com/chiangchou/p/java-proxy.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Java代理模式/静态代理/动态代理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 内核模块可选信息
- 下一篇: linux中的和||(linux中=和=