zoukankan      html  css  js  c++  java
  • Android开发中无处不在的设计模式——动态代理模式

    继续更新设计模式系列。写这个模式的主要原因是近期看到了动态代理的代码。
    先来回想一下前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中大量的用到,可是在client,因为考虑到性能的问题,所以用动态代理都会谨慎考虑,可是,一旦动态代理用的好,就会产生不一样的效果,就比方这个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的源代码。

  • 相关阅读:
    ubuntu16.04 下安装opencv2.4.9
    ubuntu16.04下CMake学习
    Ubuntu 16.04上用CMake图形界面交叉编译树莓派的OpenCV3.0
    BMP图片的C++水印算法
    OpenCV 2 学习笔记(9): 定义ROI(regions of interest):给图像加入水印
    OpenCV入门教程
    免费、高性能的人脸检测库(二进制)
    DBCP与C3P0数据库连接池
    (android高仿系列)今日头条 --新闻阅读器 (三) 完结 、总结 篇
    hadoop之WordCount源代码分析
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7363377.html
Copyright © 2011-2022 走看看