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的源代码。

  • 相关阅读:
    warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
    Windows10+CLion+OpenCV4.5.2开发环境搭建
    Android解决部分机型WebView播放视频全屏按钮灰色无法点击、点击全屏白屏无法播放等问题
    MediaCodec.configure Picture Width(1080) or Height(2163) invalid, should N*2
    tesseract
    Caer -- a friendly API wrapper for OpenCV
    Integrating OpenCV python tool into one SKlearn MNIST example for supporting prediction
    Integrating Hub with one sklearn mnist example
    What is WSGI (Web Server Gateway Interface)?
    Hub --- 机器学习燃料(数据)的仓库
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7363377.html
Copyright © 2011-2022 走看看