Android开发中无处不在的设计模式——动态代理模式

来源:互联网 发布:淘宝可以领券的网站 编辑:程序博客网 时间:2024/04/29 23:51

继续更新设计模式系列,写这个模式的主要原因是最近看到了动态代理的代码。
先来回顾一下前5个模式:
- Android开发中无处不在的设计模式——单例模式
- Android开发中无处不在的设计模式——Builder模式
- Android开发中无处不在的设计模式——观察者模式
- Android开发中无处不在的设计模式——原型模式
- Android开发中无处不在的设计模式——策略模式

动态代理模式在Java WEB中的应用简直是随处可见,尤其在Spring框架中大量的用到了动态代理;算是最重要的一个设计模式,也是最难理解的设计模式之一。

那么什么叫动态代理呢

代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。

当前的网络请求库多种多样,其中Square公司的OkHttp简直是完美的一个网络请求库,而在其上又封装了一层的Retrofit库,为方便快捷的调用Restful Api提供了一种捷径。如果你用过Retrofit,一定不会忘记有会有这么一个过程:

  • 首先定义一个接口,接口中定义网络请求的具体方法,在方法上通过注解配置host,header,params等信息。

  • 然后新建一个Retrofit对象,通过该对象产生一个你定义的接口对象。

  • 通过接口对象调用具体的方法完成请求。

就像这样子:

public interface GitHubService {  @GET("users/{user}/repos")  Call<List<Repo>> listRepos(@Path("user") String user);}
Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com")    .build();GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

那么你有没有想过一个问题,接口是不可以直接new出来的,GitHubService接口的实例是如何产生的呢,retrofit.create方法内部到底做了什么呢。没错,答案就是动态代理。该对象是程序运行期生成的代理对象。

动态代理虽然在Java WEB中大量的用到,但是在客户端,由于考虑到性能的问题,所以用动态代理都会慎重考虑,但是,一旦动态代理用的好,就会产生不一样的效果,就比如这个Retrofit库。下面,我们实现一个Retrofit的最最简易的版本。过一下动态代理的原理。由于是简易版,所以很多东西和Retrofit还是有差距的,自然也没有Retrofit那么方便,这点无视就好了。我们就以实现上面那个例子为例:

首先说明一点,我们的请求是异步的,所以返回值我们使用void,增加一个回调的参数,约定最后一个参数是回调。

public interface Callback<T> {    void onSuccess(Object t);    void onFailed(Exception e);}

最终的接口定义会是这个样子。

public interface GithubService {    @GET("users/{user}/repos")    void listRepos(@Path("user") String user,Callback<List<Repo>> 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> 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方法的实现了。原理就是先拿到最后一个参数,也就是回调,再拿到方法上的注解,获得具体的值,然后拿到除了回调之外的其他参数,获得参数上的注解,然后根据注解取得对应的值,还有原来的参数值,将方法上的注解的值中进行替换。使用OkHttp构造请求,请求完成后根据将结果解析为回调中的类型。整个过程如下

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) {                        /**                         * 获得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<List<Repo>>() {    @Override    public void onSuccess(Object t) {        System.out.println(t);    }    @Override    public void onFailed(Exception e) {    }});

这只是Retrofit中最简单的一个模块实现,如果对其他内容感兴趣,可以阅读retrofit的源码。

3 0
原创粉丝点击