Android開發中無處不在的設計模式——動態代理模式

繼續更新設計模式系列。寫這個模式的主要原因是近期看到了動態代理的代碼。
先來回想一下前5個模式:
- Android開發中無處不在的設計模式——單例模式
- Android開發中無處不在的設計模式——Builder模式
- Android開發中無處不在的設計模式——觀察者模式
- Android開發中無處不在的設計模式——原型模式
- Android開發中無處不在的設計模式——策略模式

動態代理模式在Java WEB中的應用簡直是隨處可見。尤其在Spring框架中大量的用到了動態代理;算是最重要的一個設計模式。也是最難理解的設計模式之中的一個。

那么什么叫動態代理呢

代理類在程序執行前不存在、執行時由程序動態生成的代理方式稱為動態代理。

當前的網絡請求庫多種多樣。當中Square公司的OkHttp簡直是完美的一個網絡請求庫,而在其上又封裝了一層的Retrofit庫,為方便快捷的調用Restful Api提供了一種捷徑。假設你用過Retrofit。一定不會忘記有會有這么一個過程:

  • 首先定義一個接口。接口中定義網絡請求的詳細方法。在方法上通過注解配置host,header。params等信息。

  • 然后新建一個Retrofit對象,通過該對象產生一個你定義的接口對象。

  • 通過接口對象調用詳細的方法完畢請求。

就像這樣子:


public interface GitHubService {@GET("users/{user}/repos")Call<List<Repo>> listRepos(@Path("user") String user);}

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com").build();GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> repos = service.listRepos("octocat");

那么你有沒有想過一個問題,接口是不能夠直接new出來的。GitHubService接口的實例是怎樣產生的呢。retrofit.create方法內部究竟做了什么呢。沒錯。答案就是動態代理。該對象是程序執行期生成的代理對象。

動態代理盡管在Java WEB中大量的用到,可是在client,因為考慮到性能的問題,所以用動態代理都會謹慎考慮,可是,一旦動態代理用的好,就會產生不一樣的效果,就比方這個Retrofit庫。以下,我們實現一個Retrofit的最最簡易的版本號。過一下動態代理的原理。因為是簡易版,所以非常多東西和Retrofit還是有差距的,自然也沒有Retrofit那么方便,這點無視就好了。我們就以實現上面那個樣例為例:

首先說明一點,我們的請求是異步的,所以返回值我們使用void,添加一個回調的參數,約定最后一個參數是回調。

public interface Callback<T> {void onSuccess(Object t);void onFailed(Exception e);}

終于的接口定義會是這個樣子。


public interface GithubService {@GET("users/{user}/repos")void listRepos(@Path("user") String user,Callback<List<Repo>> callback);/*** 約定最后一個參數是callback*/}

用到了兩個注解。一個是方法注解,一個是參數注解


@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface GET {String value() default "";}

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface Path {String value();}

Repo實體類是使用GsonFormat依據json自己主動生成的。

然后我們編寫Retrofit類,這個類應該是一個builder模式。里面能夠設置baseUrl,姑且忽略其它全部參數。另一個create方法。則原型例如以下:

public class Retrofit {private String baseUrl;private Retrofit(Builder builder) {this.baseUrl = builder.baseUrl;}public <T> T create(Class<T> clazz) {return null}static class Builder {private String baseUrl;Builder baseUrl(String host) {this.baseUrl = host;return this;}Retrofit build() {return new Retrofit(this);}}
}

最最關鍵的內容就是create方法的實現了。原理就是先拿到最后一個參數,也就是回調。再拿到方法上的注解,獲得詳細的值。然后拿到除了回調之外的其它參數,獲得參數上的注解,然后依據注解取得相應的值。還有原來的參數值。將方法上的注解的值中進行替換。使用OkHttp構造請求,請求完畢后依據將結果解析為回調中的類型。整個步驟例如以下

public <T> T create(Class<T> clazz) {/*** 緩存中去*/Object o = serviceMap.get(clazz);/*** 取不到則取構造代理對象*/if (o == null) {o = (T) Proxy.newProxyInstance(Retrofit.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {final Callback<?> callback = (Callback<?

>) args[args.length - 1]; final GET get = method.getAnnotation(GET.class); if (get != null) { /** * 獲得GET注解的值 */ String getValue = get.value(); System.out.println(getValue); /** * 獲得全部參數上的注解 */ Annotation[][] methodParameterAnnotationArrays = method.getParameterAnnotations(); if (methodParameterAnnotationArrays != null) { int count = methodParameterAnnotationArrays.length; for (int i = 0; i < count; i++) { /** * 獲得單個參數上的注解 */ Annotation[] methodParameterAnnotations = methodParameterAnnotationArrays[i]; if (methodParameterAnnotations != null) { for (Annotation methodParameterAnnotation : methodParameterAnnotations) { /** * 假設是Path注解 */ if (methodParameterAnnotation instanceof Path) { /** * 取得path注解上的值 */ Path path = (Path) methodParameterAnnotation; String pathValue = path.value(); System.out.println(pathValue); /** * 這是相應的參數的值 */ System.out.println(args[i]); Request.Builder builder = new Request.Builder(); /** * 使用path注解替換get注解中的值為參數值 */ String result = getValue.replaceAll("\\{" + pathValue + "\\}", (String) args[i]); System.out.println(result); /** * 開始構造請求 */ Request request = builder.get() .url(baseUrl + "/" + result) .build(); okHttpClient.newCall(request).enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, IOException e) { /** * 失敗則回調失敗的方法 */ callback.onFailed(e); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { /** * 請求成功 */ String body = response.body().string(); /** * 使用fastjson進行zhuan轉換 */ Type type = callback.getClass().getGenericInterfaces()[0]; Object o1 = JSON.parse(body); /** * 回調成功 */ callback.onSuccess(o1); } } }); } } } } } } return null; } }); /** * 扔到緩存中 */ serviceMap.put(clazz, o); } return (T) o; }

然后我們就能夠依據Retrofit那樣進行調用了

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com").build();GithubService githubService = retrofit.create(GithubService.class);githubService.listRepos("lizhangqu", new Callback<List<Repo>>() {@Overridepublic void onSuccess(Object t) {System.out.println(t);}@Overridepublic void onFailed(Exception e) {}
});

這僅僅是Retrofit中最簡單的一個模塊實現,假設對其它內容感興趣,能夠閱讀retrofit的源代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455380.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455380.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455380.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用于MPEG-4視聽流的RTP負載格式

MPEG-4的rtp協議封裝英文原版 RFC 3016&#xff1a;http://www.rfc-editor.org/rfc/rfc3016.txt中文翻譯&#xff1a;組織&#xff1a;中國互動出版網&#xff08;http://www.china-pub.com/&#xff09;RFC文檔中文翻譯計劃&#xff08;http://www.china-pub.com/compters/emo…

pycharm python 模板配置_windows下pycharm安裝、創建文件、配置默認模板

本文為大家分享了windows下pycharm安裝、創建文件、配置默認模板的具體步驟&#xff0c;供大家參考&#xff0c;具體內容如下步驟&#xff1a;下包 —->安裝——>創建文件—->定制模板一、下包官方地址這里有企業版和社區版&#xff0c;老司機都知道社區版是免費的&am…

JavaWeb筆記02-Tomcat

今日內容 web相關概念回顧web服務器軟件:TomcatServlet入門學習 web相關概念回顧 軟件架構 C/S: 客戶端/服務器端B/S: 瀏覽器/服務器端 資源分類 靜態資源: 所有用戶訪問后,得到的結果都是一樣的,成為靜態資源,靜態資源可以直接被瀏覽器解析 如:html, css ,JavaScript 動態資…

網上的畫板代碼收集和整理

修改后的代碼[1]為&#xff0c;少了一個} package com.example.administrator.myapplication;import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.graphics.Canvas; import an…

如何寫年終總結(轉)

很多人不重視年終總結&#xff0c;覺得是一個非常令人厭煩的任務&#xff0c;往往是應付了事&#xff0c;短短幾百字&#xff0c;對目前工作中存在的問題發現不夠&#xff0c;思考不足&#xff0c;對自己一年的評價和未來一年的定位沒有說明。造成的后果就是公司得不到來自基層…

cad移動時捕捉不到基點_CAD入門必備(一)移動和復制新手必看

cad也瘋狂前言&#xff1a;CAD繪圖之所以能夠取代手工繪圖&#xff0c;很大的一部分原因是因為它可以很方便的修改和重復利用&#xff0c;例如外參可以節省很大部分時間。而我們在使用CAD中&#xff0c;用得最頻繁的功能就是移動和復制了&#xff0c;當然這也是新手必備的其中一…

H.264軟件解碼器在PXA270平臺上的優化

羅 嶸&#xff0c;何 苦 時間:2009年04月24日摘 要&#xff1a; 研究了嵌入式系統中H.264 Baseline軟件解碼器設計和優化的問題&#xff0c;提出了四種有效的優化方法&#xff0c;并在PXA270平臺上進行了測試。測試結果顯示&#xff0c;綜合使用提出的四種方法&#xff0c;H.26…

JavaWeb筆記04-解決GET與POST亂碼問題

解決GET與POST亂碼問題: 請求的亂碼問題 GET:tomcat8版本之前,get請求會亂碼 正常文字 --> UTF-8編碼 --> 字節數組 --> ISO-8859-1 編碼 --> 亂碼文字 正常文字 <-- UTF-8編碼 <-- 字節數組 <-- ISO-8859-1 編碼 <-- 亂碼文字解決亂碼的兩種格式: …

EF架構~codeFirst從初始化到數據庫遷移

一些介紹 CodeFirst是EntityFrameworks的一種開發模式&#xff0c;即代碼優先&#xff0c;它以業務代碼為主&#xff0c;通過代碼來生成數據庫&#xff0c;并且加上migration的強大數據表比對功能來生成數據庫版本&#xff0c;讓程序開發人員不用維護數據庫的變更&#xff0c;而…

Ubuntu用戶Steam控制器不工作的解決辦法

Steam 控制器已開始送貨到世界各地游戲玩家手中&#xff0c;不過有朋友遇到 Steam 控制器在 Ubuntu 中無法正常工作&#xff0c;本文我們來介紹一下解決辦法。該解決辦法并非 Ubuntu 官方提出的最佳解決方案&#xff0c;不過還是可以臨時解決 Ubuntu 用戶 Steam 控制器不工作的…

lisp 車位塊自動編號_機械車位做產權登記,真的適合嗎?

為了更好地把握停車市場發展動向&#xff0c;給停車行業從業者提供一個發表觀點、各抒己見的平臺&#xff0c;共同促進停車行業的發展&#xff0c;《城市停車》開設熱點版塊&#xff0c;每期針對1-2個行業熱點&#xff0c;廣泛征集業內人士觀點和看法。HOT TOPIC本期熱點今年兩…

JavaWeb筆記05-解決線程安全問題

線程安全問題: Servlet的service方法,每次被請求是,調用. 這個調用很特殊,是在新的子線程中調用的,當service方法執行完畢,子線程死亡了. 可以簡單的理解為:service方法每次執行都是一個新的線程. package cn.xdl.demo1;import javax.servlet.ServletException; import jav…

Java并發編程實戰 第14章 構建自定義的同步工具

狀態依賴性 定義&#xff1a;只有滿足特定的狀態才能繼續執行某些操作&#xff08;這些操作依賴于固定的狀態&#xff0c;這些狀態需要等待別的線程來滿足&#xff09;。 FutureTask&#xff0c;Semaphroe&#xff0c;BlockingQueue等&#xff0c;都是狀態依賴性的類。 條件隊列…

webserver接口_SpringBoot內置源碼解析WebServer初始化過程

WebServer 初始化過程在上一節中 Spring Boot 初始化了 WebServer 對應的工廠類。同時&#xff0c;我們也知道對應 Web容器的WebServer實現類有:TomcatWebServer、JettyWebServer和UndertowWebServer。這節重點講解這些 WebServer 是如何被初始化&#xff0c;又如何啟動的。Web…

提升應用程序彈性:保障工作負載正常運行

通過集群化、復制、快照、微服務和應用程序設計來提高企業工作負載的應用程序彈性和可用性。 應用程序的彈性和可用性是現代企業工作負載的關鍵屬性。應用程序需要在硬件故障發生后&#xff0c;扛過服務故障(例如負載平衡器和域名系統錯誤)保持工作狀態&#xff0c;并且可以忍受…

JDBC筆記01-JDBC,Connection,Statement,ResultSet,PreparedStatement,Properties

學習目標 理解JDBC原理 掌握Connection接口的使用 掌握Statement接口的使用 掌握ResultSet接口的使用 掌握PreparedStatement接口的使用 掌握Properties類與配置文件的使用 JDBC 概念 JDBC (Java DataBase Connectivity) Java數據庫連接技術的簡稱&#xff0c;提供連接各種常…

NVDKC6416平臺H.264算法優化

本文轉載自&#xff1a;http://blog.csdn.net/embedesign/archive/2009/09/15/4556486.aspx&#xff0c;版權歸原作者&#xff0c;編輯&#xff1a;小乙哥 多媒體通信終端設備具有廣泛的應用前景&#xff0c;可以應用于視頻會議、可視電話、PDA、數字電視等各個領域&#xff0…

攔截器及 Spring MVC 整合

一、實驗介紹 1.1 實驗內容 本節課程主要利用 Spring MVC 框架實現攔截器以及 Spring MVC 框架的整合。 1.2 實驗知識點 Spring MVC 框架攔截器1.3 實驗環境 JDK1.8Eclipse JavaEE二、實驗步驟 2.1 攔截器實現 在項目 hrms 的目錄 src/main/java 下新建包 com.shiyanlou.interc…

高德地圖軌跡回放_高德地圖上線了一個新功能….

文、路人甲TM德地圖這兩天剛上線了一個叫做「家人地圖」的功能&#xff0c;所謂家人地圖顧名思義&#xff0c;就是你可以通過高德地圖組建一個家人圈&#xff0c;在這個圈子里面你可以看到你的家人在什么位置&#xff0c;當你的家人到達什么位置的時候自動發送通知或者警告&…

You have new mail in /var/spool/mail/root消除提示的方法

有時在進入系統的時候經常提示You have new mail in /var/spool/mail/root 你覺得煩人---解決方法&#xff1a; 修改系統配置文件/etc/profile&#xff0c;告訴系統不要去檢查郵箱. 具體操作&#xff1a;命令行輸入&#xff1a;echo "unset MAILCHECK" >> /etc…