繼續更新設計模式系列。寫這個模式的主要原因是近期看到了動態代理的代碼。
先來回想一下前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的源代碼。