都知道Retrofit
是通過動(dòng)態(tài)代理來生成代理對(duì)象作為網(wǎng)絡(luò)請(qǐng)求的發(fā)起者。
今天就來看下動(dòng)態(tài)代理是怎么操作的。或者說是怎么讓一個(gè)貌似接口的對(duì)象調(diào)用它的抽象方法呢?
先來看代碼
public static void main(String[] args) {
Factory factory = new Factory();
Bird bird = factory.create(Bird.class);
bird.fly();
}
interface Bird {
void fly();
}
這里代碼通過一個(gè)Factory
實(shí)例調(diào)用create
方法,傳入一個(gè)接口的class
對(duì)象就可以返回一個(gè)接口的實(shí)例,可以調(diào)用接口中的方法fly()
。
而在我們的靜態(tài)代碼中并沒有一個(gè)類去實(shí)現(xiàn)了這個(gè)Bird
接口(完整代碼可以看下方)。那么這個(gè)對(duì)象到底是從哪里來的呢?
完整代碼如下
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;
}
}
}
在調(diào)用bird.fly()
時(shí),輸出結(jié)果為flying...
,很明顯,代碼中就如同開始所說的,并不存在一個(gè)實(shí)現(xiàn)了Bird
接口的子類,而bird
又實(shí)實(shí)在在調(diào)用了fly()
方法。唯一的可能就是bird
是接口的實(shí)例(或者說實(shí)現(xiàn)接口的子類的對(duì)象)。這里看起來似乎就有些詭異了。
當(dāng)然編程沒有魔法,這里只是利用到了Java
的動(dòng)態(tài)代理,通過Proxy.newProxyInstance()
方法生成實(shí)現(xiàn)了指定接口的子類,然后返回了這個(gè)動(dòng)態(tài)生成類的實(shí)例對(duì)象。
這個(gè)子類在調(diào)用接口中的方法時(shí),其實(shí)調(diào)用的是InvocationHandler
的invoke()
方法。在此方法中會(huì)有對(duì)應(yīng)參數(shù)的回調(diào),可以根據(jù)這些參數(shù)做出合適的攔截/增強(qiáng)等操作。
要留意的一點(diǎn)是JDK
提供的動(dòng)態(tài)代理,動(dòng)態(tài)生成的子類是繼承自Proxy
類的,,而Java
是不支持多繼承的,所以很顯然。通過動(dòng)態(tài)代理返回的對(duì)象必然是以接口形式來接收的,擴(kuò)展的只有接口和實(shí)現(xiàn)接口的子類,對(duì)于一些沒有實(shí)現(xiàn)接口的類是沒有辦法進(jìn)行擴(kuò)展的。(cglib
支持?jǐn)U展類)
知道了這些也就明白了Retrofit
的動(dòng)態(tài)代理大致是個(gè)什么邏輯。
下面仿造Retrofit
通過方法上注解來模擬一次網(wǎng)絡(luò)請(qǐng)求吧。
通過在接口中在方法上的注解,確定一些請(qǐng)求的參數(shù)。然后創(chuàng)建代理對(duì)象,在方法中拼接處完整的Url
, https://github.com/search?q=java
請(qǐng)求網(wǎng)絡(luò),并且在響應(yīng)頭中的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 "";
}