分享

动态代理在 Retrofit 中的使用

 大船说车 2022-07-27 发布于四川

首先,什么是动态代理和为什么会有动态代理。

众所周知,Java 是一门静态语言,编写完的类,无法在运行时做动态修改。

一个简单的动态代理如下:

1、先定义一个接口,想要使用动态代理,必须先定义一个接口:

1public interface IHello{
2    void hello();
3}

2、再让想要动态代理的类实现接口:

1public class Hello implements IHello{
2    public void hello(){
3        System.out.print("hello");
4    }
5}

3、再定义一个实现InvocationHandler的类:

 1public class MyInvocationHandler implements InvocationHandler{
2    private Object obj;
3    public MyInvocationHandler(Object obj){
4        this.obj = obj;
5    }
6    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
7        //在原方法前边动态插入代码
8        Object result = method.invoke(target, args);
9        //在原方法后边动态插入代码
10        return result;
11    }
12}

InvocationHandler是 Java 提供的动态代理实现类,想要使用动态代理,必须实现该接口,重写invoke方法,并酌情在原方法的前边或后边插入需要的代码,method.invoke(target, args)会调用原方法(比如 Hello 类中的 hello()方法)。

上边就是一个简单的动态代理的例子。

Retrofit 中也使用了动态代理,我们以官方提供的示例代码为例,首先声明一个接口:

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

然后在需要网络请求的地方调用如下代码:

1Retrofit retrofit = new Retrofit.Builder()
2    .baseUrl("https://api.github.com/")
3    .build();
4
5GitHubService service = retrofit.create(GitHubService.class);
6
7Call<List<Repo>> repos = service.listRepos("octocat");

上边定义的GithubService等同于前文的IHello接口,一个类想要做动态代理,就必须先要定义一个接口。

然后关键代码是retrofit,表面上看这与上文提到的写法完全对应不上,其实这里只是封装起来了,查看它的源码就能发现端倪。

 1public <T> create(final Class<T> service) {
2    Utils.validateServiceInterface(service);
3    if (validateEagerly) {
4      eagerlyValidateMethods(service);
5    }
6    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
7        new InvocationHandler() {
8          private final Platform platform = Platform.get();
9
10          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
11              throws Throwable 
{
12            // If the method is a method from Object then defer to normal invocation.
13            if (method.getDeclaringClass() == Object.class) {
14              return method.invoke(this, args);
15            }
16            if (platform.isDefaultMethod(method)) {
17              return platform.invokeDefaultMethod(method, service, proxy, args);
18            }
19            ServiceMethod<Object, Object> serviceMethod =
20                (ServiceMethod<Object, Object>) loadServiceMethod(method);
21            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
22            return serviceMethod.adapt(okHttpCall);
23          }
24        });
25  }

看到new InvocationHandler(){****}了吧,等于变相实现了InvocationHandler,另外,Proxy.newProxyInstance最终会调用一个 native 方法,动态生成一个 GitHubService(我们前文声明的接口)的实现类,大致长这样(这是 Java 动态生成的,所以以下代码并不完全准确):

 1  public final class $Proxy0 extends Proxy implements GitHubService {  
2    private static Method m1;  
3    private static Method m0;  
4    private static Method m3;  
5    private static Method m2;  
6
7    static {  
8        try {  
9            m1 = Class.forName("java.lang.Object").getMethod("equals",  
10                    new Class[] { Class.forName("java.lang.Object") });  
11
12            m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
13                    new Class[0]);  
14
15            m3 = Class.forName("***.GitHubService").getMethod("request",  
16                    new Class[0]);  
17
18            m2 = Class.forName("java.lang.Object").getMethod("toString",  
19                    new Class[0]);  
20
21        } catch (NoSuchMethodException nosuchmethodexception) {  
22            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
23        } catch (ClassNotFoundException classnotfoundexception) {  
24            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
25        }  
26    }
27
28    public $Proxy0(InvocationHandler invocationhandler) {  
29        super(invocationhandler);  
30    }  
31
32    @Override  
33    public final boolean equals(Object obj) {  
34        try {  
35            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
36        } catch (Throwable throwable) {  
37            throw new UndeclaredThrowableException(throwable);  
38        }  
39    }  
40
41    @Override  
42    public final int hashCode() {  
43        try {  
44            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
45        } catch (Throwable throwable) {  
46            throw new UndeclaredThrowableException(throwable);  
47        }  
48    }  
49
50    public final void listRepos() {  
51        try {  
52            super.h.invoke(this, m3, null);  
53            return;  
54        } catch (Error e) {  
55        } catch (Throwable throwable) {  
56            throw new UndeclaredThrowableException(throwable);  
57        }  
58    }  
59
60    @Override  
61    public final String toString() {  
62        try {  
63            return (String) super.h.invoke(this, m2, null);  
64        } catch (Throwable throwable) {  
65            throw new UndeclaredThrowableException(throwable);  
66        }  
67    }  
68

可以发现 Java 帮我们生成的类中重写了我们GithubService接口的listRepos(),该方法中会调用super.h.invoke(this, m3, null);,就是调用父类的hinvoke(),它的父类是Proxyh是一个InvocationHandler对象;

1/**
2* the invocation handler for this proxy instance.
3@serial
4*/

5protected InvocationHandler h;

这个h,就是上文提到的new InvocationHandler(){}

 1public <T> create(final Class<T> service) {
2    Utils.validateServiceInterface(service);
3    if (validateEagerly) {
4      eagerlyValidateMethods(service);
5    }
6    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
7        new InvocationHandler() {
8          private final Platform platform = Platform.get();
9
10          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
11              throws Throwable 
{
12            // If the method is a method from Object then defer to normal invocation.
13            if (method.getDeclaringClass() == Object.class) {
14              return method.invoke(this, args);
15            }
16            if (platform.isDefaultMethod(method)) {
17              return platform.invokeDefaultMethod(method, service, proxy, args);
18            }
19            ServiceMethod<Object, Object> serviceMethod =
20                (ServiceMethod<Object, Object>) loadServiceMethod(method);
21            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
22            return serviceMethod.adapt(okHttpCall);
23          }
24        });
25  }

最后,简单画一个图总结下流程:


    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多