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

來源:互聯網
上載者:User

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

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

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

那麼什麼叫動態代理呢

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

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

首先定義一個介面,介面中定義網路請求的具體方法,在方法上通過註解配置host,header,params等資訊。

然後建立一個Retrofit對象,通過該對象產生一個你定義的介面對象。

通過介面對象調用具體的方法完成請求。

就像這樣子:

> listRepos(@Path("user") String user);}" data-snippet-id="ext.fadc3883ecfd2cd1a1ca67e15e7b1971" data-snippet-saved="false" data-csrftoken="5VgX1Wh4-W-l4cPYta7C6PsnrxDn_HUux6Fk">public interface GitHubService {  @GET("users/{user}/repos")  Call> listRepos(@Path("user") String user);}
Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com")    .build();GitHubService service = retrofit.create(GitHubService.class);
> repos = service.listRepos("octocat");" data-snippet-id="ext.ee6b799b6e05337f344653d3f6028237" data-snippet-saved="false" data-csrftoken="zatspdc2-6xr9Qf-RbSgSei_XQsqIqeqXvQQ">Call> repos = service.listRepos("octocat");

那麼你有沒有想過一個問題,介面是不可以直接new出來的,GitHubService介面的執行個體是如何產生的呢,retrofit.create方法內部到底做了什麼呢。沒錯,答案就是動態代理。該對象是程式運行期產生的代理對象。

動態代理雖然在Java WEB中大量的用到,但是在用戶端,由於考慮到效能的問題,所以用動態代理都會謹慎考慮,但是,一旦動態代理用的好,就會產生不一樣的效果,就比如這個Retrofit庫。下面,我們實現一個Retrofit的最最簡易的版本。過一下動態代理的原理。由於是簡易版,所以很多東西和Retrofit還是有差距的,自然也沒有Retrofit那麼方便,這點無視就好了。我們就以實現上面那個例子為例:

首先說明一點,我們的請求是非同步,所以傳回值我們使用void,增加一個回調的參數,約定最後一個參數是回調。

 {    void onSuccess(Object t);    void onFailed(Exception e);}" data-snippet-id="ext.a886f274abbdc51fa06b7d1abed39036" data-snippet-saved="false" data-csrftoken="notDIIJF-5EKuT6X6LWWipbRR2DHvKJcZYxM">public interface Callback {    void onSuccess(Object t);    void onFailed(Exception e);}

最終的介面定義會是這個樣子。

> callback);    /**     * 約定最後一個參數是callback     */}" data-snippet-id="ext.f115dcec337f47ddaaf65522ab40a2e0" data-snippet-saved="false" data-csrftoken="hOCzSeOZ-2RNrlcsbRh9aW4trRlUk_l6C1D4">public interface GithubService {    @GET("users/{user}/repos")    void listRepos(@Path("user") String user,Callback> 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 create(Class 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 create(Class clazz) {        /**         * 緩衝中去         */        Object o = serviceMap.get(clazz);        /**         * 取不到則取構造代理對象         */        if (o == null) {            o = (T) Proxy.newProxyInstance(Retrofit.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {                @Override                public 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>() {    @Override    public void onSuccess(Object t) {        System.out.println(t);    }    @Override    public void onFailed(Exception e) {    }});

這隻是Retrofit中最簡單的一個模組實現,如果對其他內容感興趣,可以閱讀retrofit的源碼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.