代理类在运行前不存在,运行时由程序动态生成的代理方式称为动态代理
关于Retrofit Square公司的OkHttp简直是完美的一个网络请求库,而在其上又封装了一层的Retrofit库,使其调用Restful Api更方便
简述Retrofit调用流程 因为本篇只是简述动态代理在Retrofit的使用,所以不过多总结Retrofit的详细使用,在上家公司的项目都网络层,用的retrofit,现在这家公司的项目因为刚开始是我独立开发,所以网络层框架我也是用的Retrofit,功能强大,详细使用,以后再总结
熟悉Retrofit使用的都对下面几个步骤比较熟悉了:
定义一个ApiService接口,通过注解可以标记请求方法,请求参数,以及添加的头信息
然后创建Retrofit对象,通过建造者模式设置BaseUrl等一些参数,通过Retrofit对象create一个你定义的接口对象
拿到接口对象调用具体的方法完成请求
具体的代码大概如下:
1 2 3 4 public interface ApiService { @GET ("users/{user}/repos" ) Call<List<Repo>> listRepos(@Path ("user" ) String user); }
1 2 3 4 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com" ) .build(); ApiService service = retrofit.create(ApiService.class);
1 Call<List<Repo>> repos = service.listRepos("fengan" );
ApiService是如何产生的 因为接口是不可以直接new出来的,那么ApiService是如何产生的呢?
ApiService service = retrofit.create(ApiService.class);方法内部到底做了什么?
没错,就是动态代理
为了更好的理解动态代理,下面过一下简易版的Retrofit,
a v 1 2 3 4 5 6 7 public interface Callback<T> { void onSuccess(Object t); void onFailed(Exception e); }
1 2 3 4 5 6 7 8 9 public interface GithubService { @GET ("users/{user}/repos" ) void list<Repos>(@Path ("user" ) String user,Callback<List<Repo>> callback); }
用到了两个注解,一个是方法注解,一个是参数注解
1 2 3 4 5 6 7 8 9 @Retention (RetentionPolicy.RUNTIME)@Target ({ElementType.METHOD})public @interface GET { String value () default "" ; }
1 2 3 4 5 6 7 8 9 @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.PARAMETER)public @interface Path { String value () ; }
Repo实体类是使用GsonFormat根据json自动生成的。现实使用中,我们在构建Retrofit过程传入GsonFactory
Retrofit这个类应该是一个builder模式,里面可以设置baseUrl,姑且忽略其他所有参数。还有一个create方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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这个方法了,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 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() { @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 ) { 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) { if (methodParameterAnnotation instanceof Path) { Path path = (Path) methodParameterAnnotation; String pathValue = path.value(); System.out.println(pathValue); System.out.println(args[i]); Request.Builder builder = new Request.Builder(); 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(); Type type = callback.getClass().getGenericInterfaces()[0 ]; Object o1 = JSON.parse(body); callback.onSuccess(o1); } } }); } } } } } } return null ; } }); serviceMap.put(clazz, o); } return (T) o; }
然后我们就可以根据Retrofit那样进行调用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com" ) .build(); GithubService githubService = retrofit.create(GithubService.class); githubService.listRepos("lizhangqu" , new Callback<List<Repo>>() { @Override public void onSuccess (Object t) { System.out.println(t); } @Override public void onFailed (Exception e) { } });
retrofit动态代理原理 原理就是先拿到最后一个参数,也就是回调,再拿到方法上的注解,获得具体的值,然后拿到除了回调之外的其他参数,获得参数上的注解,然后根据注解取得对应的值,还有原来的参数值,将方法上的注解的值中进行替换。使用OkHttp构造请求,请求完成后根据将结果解析为回调中的类型。整个过程如下
拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。
java中的动态代理 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
1 2 3 4 5 Object invoke (Object proxy, Method method, Object[] args) throws Throwable proxy: 指代我们所代理的那个真实对象 method: 指代的是我们所要调用真实对象的某个方法的Method对象 args: 指代的是调用真实对象某个方法时接受的参数
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
1 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
1 2 3 4 5 6 7 8 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentExceptionloader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态) ,这样我就能调用这组接口中的方法了 h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
使用如下:
1 2 3 4 5 6 public interface Subject { public void rent () ; public void hello (String str) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class RealSubject implements Subject { @Override public void rent () { System.out.println("I want to rent my house" ); } @Override public void hello (String str) { System.out.println("hello: " + str); } }
定义动态代理类,任何动态代理类都必须实现InvotionHandler这个接口,重写invoke方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class DynamicProxy implements InvocationHandler { private Object subject; public DynamicProxy (Object subject) { this .subject = subject; } @Override public Object invoke (Object object, Method method, Object[] args) throws Throwable { System.out.println("before rent house" ); System.out.println("Method:" + method); method.invoke(subject, args); System.out.println("after rent house" ); return null ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Client { public static void main (String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new DynamicProxy(realSubject); Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName()); subject.rent(); subject.hello("world" ); } }