Retrofit的動態代理

都知道Retrofit是通過動態代理來生成代理對象作為網絡請求的發起者。

今天就來看下動態代理是怎么操作的?;蛘哒f是怎么讓一個貌似接口的對象調用它的抽象方法呢?

先來看代碼

public static void main(String[] args) {
  Factory factory = new Factory();
  Bird bird = factory.create(Bird.class);
  bird.fly();
}

interface Bird {
  void fly();
}

這里代碼通過一個Factory 實例調用create方法,傳入一個接口的class對象就可以返回一個接口的實例,可以調用接口中的方法fly()。

而在我們的靜態代碼中并沒有一個類去實現了這個Bird接口(完整代碼可以看下方)。那么這個對象到底是從哪里來的呢?

完整代碼如下

public class DynamicProxy {

    interface Bird {
        void fly();
    }

    public static void main(String[] args) {
        Factory factory = new Factory();
        Bird bird = factory.create(Bird.class);
        bird.fly();
    }

    static class Factory implements InvocationHandler {

        public <T> T create(Class<T> target) {
            return (T) Proxy.newProxyInstance(target.getClassLoader(),
                    new Class[]{target},
                    this);
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("flying...");
            return null;
        }
    }
}

在調用bird.fly()時,輸出結果為flying...,很明顯,代碼中就如同開始所說的,并不存在一個實現了Bird接口的子類,而bird又實實在在調用了fly()方法。唯一的可能就是bird是接口的實例(或者說實現接口的子類的對象)。這里看起來似乎就有些詭異了。

當然編程沒有魔法,這里只是利用到了Java的動態代理,通過Proxy.newProxyInstance()方法生成實現了指定接口的子類,然后返回了這個動態生成類的實例對象。

這個子類在調用接口中的方法時,其實調用的是InvocationHandlerinvoke()方法。在此方法中會有對應參數的回調,可以根據這些參數做出合適的攔截/增強等操作。

要留意的一點是JDK提供的動態代理,動態生成的子類是繼承自Proxy類的,,而Java是不支持多繼承的,所以很顯然。通過動態代理返回的對象必然是以接口形式來接收的,擴展的只有接口和實現接口的子類,對于一些沒有實現接口的類是沒有辦法進行擴展的。(cglib支持擴展類)

知道了這些也就明白了Retrofit的動態代理大致是個什么邏輯。

下面仿造Retrofit通過方法上注解來模擬一次網絡請求吧。

通過在接口中在方法上的注解,確定一些請求的參數。然后創建代理對象,在方法中拼接處完整的Url, https://github.com/search?q=java

請求網絡,并且在響應頭中的Status字段打印出來。

public interface Bird {

    @Protocol()
    @Method()
    @Path("search")
    @Query("q=java")
    @Url("github.com")
    String fly();
}


public class Test {

    public static void main(String[] args) {
        Factory factory = new Factory();
        Bird bird = factory.create(Bird.class);
        String status = bird.fly();

        System.out.println(status);//輸出 Status: 200 OK
    }
}

部分代碼

public class Factory implements InvocationHandler {
    OkHttpClient httpClient = new OkHttpClient();

    public <T> T create(Class<T> target) {
        return (T) Proxy.newProxyInstance(target.getClassLoader(),
                new Class[]{target},
                this);
    }

    public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
        String get = method.getAnnotation(Method.class).method();
        String protocol = method.getAnnotation(Protocol.class).value();
        String url = method.getAnnotation(Url.class).value();
        String path = method.getAnnotation(Path.class).value();
        String query = method.getAnnotation(Query.class).value();

        String entire_url = protocol + "://" + url + "/" + path + "?" + query;


        System.out.println(entire_url);
        Request build = new Request.Builder().url(entire_url).get().build();

        return httpClient.newCall(build).execute().headers().get("Status");
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Method {
    String method() default "GET";
}


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


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

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



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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容