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() {@Overridepublic 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>>() {@Overridepublic void onSuccess(Object t) {System.out.println(t);}@Overridepublic void onFailed(Exception e) {}
});

这仅仅是Retrofit中最简单的一个模块实现,假设对其它内容感兴趣,能够阅读retrofit的源代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用于MPEG-4视听流的RTP负载格式

MPEG-4的rtp协议封装英文原版 RFC 3016&#xff1a;http://www.rfc-editor.org/rfc/rfc3016.txt中文翻译&#xff1a;组织&#xff1a;中国互动出版网&#xff08;http://www.china-pub.com/&#xff09;RFC文档中文翻译计划&#xff08;http://www.china-pub.com/compters/emo…

pycharm python 模板配置_windows下pycharm安装、创建文件、配置默认模板

本文为大家分享了windows下pycharm安装、创建文件、配置默认模板的具体步骤&#xff0c;供大家参考&#xff0c;具体内容如下步骤&#xff1a;下包 —->安装——>创建文件—->定制模板一、下包官方地址这里有企业版和社区版&#xff0c;老司机都知道社区版是免费的&am…

JavaWeb笔记02-Tomcat

今日内容 web相关概念回顾web服务器软件:TomcatServlet入门学习 web相关概念回顾 软件架构 C/S: 客户端/服务器端B/S: 浏览器/服务器端 资源分类 静态资源: 所有用户访问后,得到的结果都是一样的,成为静态资源,静态资源可以直接被浏览器解析 如:html, css ,JavaScript 动态资…

网上的画板代码收集和整理

修改后的代码[1]为&#xff0c;少了一个} package com.example.administrator.myapplication;import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.graphics.Canvas; import an…

如何写年终总结(转)

很多人不重视年终总结&#xff0c;觉得是一个非常令人厌烦的任务&#xff0c;往往是应付了事&#xff0c;短短几百字&#xff0c;对目前工作中存在的问题发现不够&#xff0c;思考不足&#xff0c;对自己一年的评价和未来一年的定位没有说明。造成的后果就是公司得不到来自基层…

cad移动时捕捉不到基点_CAD入门必备(一)移动和复制新手必看

cad也疯狂前言&#xff1a;CAD绘图之所以能够取代手工绘图&#xff0c;很大的一部分原因是因为它可以很方便的修改和重复利用&#xff0c;例如外参可以节省很大部分时间。而我们在使用CAD中&#xff0c;用得最频繁的功能就是移动和复制了&#xff0c;当然这也是新手必备的其中一…

H.264软件解码器在PXA270平台上的优化

罗 嵘&#xff0c;何 苦 时间:2009年04月24日摘 要&#xff1a; 研究了嵌入式系统中H.264 Baseline软件解码器设计和优化的问题&#xff0c;提出了四种有效的优化方法&#xff0c;并在PXA270平台上进行了测试。测试结果显示&#xff0c;综合使用提出的四种方法&#xff0c;H.26…

JavaWeb笔记04-解决GET与POST乱码问题

解决GET与POST乱码问题: 请求的乱码问题 GET:tomcat8版本之前,get请求会乱码 正常文字 --> UTF-8编码 --> 字节数组 --> ISO-8859-1 编码 --> 乱码文字 正常文字 <-- UTF-8编码 <-- 字节数组 <-- ISO-8859-1 编码 <-- 乱码文字解决乱码的两种格式: …

EF架构~codeFirst从初始化到数据库迁移

一些介绍 CodeFirst是EntityFrameworks的一种开发模式&#xff0c;即代码优先&#xff0c;它以业务代码为主&#xff0c;通过代码来生成数据库&#xff0c;并且加上migration的强大数据表比对功能来生成数据库版本&#xff0c;让程序开发人员不用维护数据库的变更&#xff0c;而…

Ubuntu用户Steam控制器不工作的解决办法

Steam 控制器已开始送货到世界各地游戏玩家手中&#xff0c;不过有朋友遇到 Steam 控制器在 Ubuntu 中无法正常工作&#xff0c;本文我们来介绍一下解决办法。该解决办法并非 Ubuntu 官方提出的最佳解决方案&#xff0c;不过还是可以临时解决 Ubuntu 用户 Steam 控制器不工作的…

lisp 车位块自动编号_机械车位做产权登记,真的适合吗?

为了更好地把握停车市场发展动向&#xff0c;给停车行业从业者提供一个发表观点、各抒己见的平台&#xff0c;共同促进停车行业的发展&#xff0c;《城市停车》开设热点版块&#xff0c;每期针对1-2个行业热点&#xff0c;广泛征集业内人士观点和看法。HOT TOPIC本期热点今年两…

JavaWeb笔记05-解决线程安全问题

线程安全问题: Servlet的service方法,每次被请求是,调用. 这个调用很特殊,是在新的子线程中调用的,当service方法执行完毕,子线程死亡了. 可以简单的理解为:service方法每次执行都是一个新的线程. package cn.xdl.demo1;import javax.servlet.ServletException; import jav…

Java并发编程实战 第14章 构建自定义的同步工具

状态依赖性 定义&#xff1a;只有满足特定的状态才能继续执行某些操作&#xff08;这些操作依赖于固定的状态&#xff0c;这些状态需要等待别的线程来满足&#xff09;。 FutureTask&#xff0c;Semaphroe&#xff0c;BlockingQueue等&#xff0c;都是状态依赖性的类。 条件队列…

webserver接口_SpringBoot内置源码解析WebServer初始化过程

WebServer 初始化过程在上一节中 Spring Boot 初始化了 WebServer 对应的工厂类。同时&#xff0c;我们也知道对应 Web容器的WebServer实现类有:TomcatWebServer、JettyWebServer和UndertowWebServer。这节重点讲解这些 WebServer 是如何被初始化&#xff0c;又如何启动的。Web…

提升应用程序弹性:保障工作负载正常运行

通过集群化、复制、快照、微服务和应用程序设计来提高企业工作负载的应用程序弹性和可用性。 应用程序的弹性和可用性是现代企业工作负载的关键属性。应用程序需要在硬件故障发生后&#xff0c;扛过服务故障(例如负载平衡器和域名系统错误)保持工作状态&#xff0c;并且可以忍受…

JDBC笔记01-JDBC,Connection,Statement,ResultSet,PreparedStatement,Properties

学习目标 理解JDBC原理 掌握Connection接口的使用 掌握Statement接口的使用 掌握ResultSet接口的使用 掌握PreparedStatement接口的使用 掌握Properties类与配置文件的使用 JDBC 概念 JDBC (Java DataBase Connectivity) Java数据库连接技术的简称&#xff0c;提供连接各种常…

NVDKC6416平台H.264算法优化

本文转载自&#xff1a;http://blog.csdn.net/embedesign/archive/2009/09/15/4556486.aspx&#xff0c;版权归原作者&#xff0c;编辑&#xff1a;小乙哥 多媒体通信终端设备具有广泛的应用前景&#xff0c;可以应用于视频会议、可视电话、PDA、数字电视等各个领域&#xff0…

拦截器及 Spring MVC 整合

一、实验介绍 1.1 实验内容 本节课程主要利用 Spring MVC 框架实现拦截器以及 Spring MVC 框架的整合。 1.2 实验知识点 Spring MVC 框架拦截器1.3 实验环境 JDK1.8Eclipse JavaEE二、实验步骤 2.1 拦截器实现 在项目 hrms 的目录 src/main/java 下新建包 com.shiyanlou.interc…

高德地图轨迹回放_高德地图上线了一个新功能….

文、路人甲TM德地图这两天刚上线了一个叫做「家人地图」的功能&#xff0c;所谓家人地图顾名思义&#xff0c;就是你可以通过高德地图组建一个家人圈&#xff0c;在这个圈子里面你可以看到你的家人在什么位置&#xff0c;当你的家人到达什么位置的时候自动发送通知或者警告&…

You have new mail in /var/spool/mail/root消除提示的方法

有时在进入系统的时候经常提示You have new mail in /var/spool/mail/root 你觉得烦人---解决方法&#xff1a; 修改系统配置文件/etc/profile&#xff0c;告诉系统不要去检查邮箱. 具体操作&#xff1a;命令行输入&#xff1a;echo "unset MAILCHECK" >> /etc…