继续更新设计模式系列。写这个模式的主要原因是近期看到了动态代理的代码。

先来回想一下前5个模式:

- Android开发中无处不在的设计模式——单例模式

- Android开发中无处不在的设计模式——Builder模式

- Android开发中无处不在的设计模式——观察者模式

- Android开发中无处不在的设计模式——原型模式

- Android开发中无处不在的设计模式——策略模式

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

那么什么叫动态代理呢

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

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

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

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

  • 通过接口对象调用详细的方法完毕请求。

就像这样子:

> listRepos(@Path("user") String user);

}
" data-snippet-id="ext.fadc3883ecfd2cd1a1ca67e15e7b1971" data-snippet-saved="false" data-csrftoken="5VgX1Wh4-W-l4cPYta7C6PsnrxDn_HUux6Fk">
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);
> repos = service.listRepos("octocat");
" data-snippet-id="ext.ee6b799b6e05337f344653d3f6028237" data-snippet-saved="false" data-csrftoken="zatspdc2-6xr9Qf-RbSgSei_XQsqIqeqXvQQ">
Call<List<Repo>> repos = service.listRepos("octocat");

那么你有没有想过一个问题,接口是不能够直接new出来的。GitHubService接口的实例是怎样产生的呢。retrofit.create方法内部究竟做了什么呢。没错。答案就是动态代理。该对象是程序执行期生成的代理对象。

动态代理尽管在Java WEB中大量的用到,可是在client,因为考虑到性能的问题,所以用动态代理都会谨慎考虑,可是,一旦动态代理用的好,就会产生不一样的效果,就比方这个Retrofit库。以下,我们实现一个Retrofit的最最简易的版本号。过一下动态代理的原理。因为是简易版,所以非常多东西和Retrofit还是有差距的,自然也没有Retrofit那么方便,这点无视就好了。我们就以实现上面那个样例为例:

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

 {

    void onSuccess(Object t);

    void onFailed(Exception e);

}
" data-snippet-id="ext.a886f274abbdc51fa06b7d1abed39036" data-snippet-saved="false" data-csrftoken="notDIIJF-5EKuT6X6LWWipbRR2DHvKJcZYxM"> public interface Callback<T> { void onSuccess(Object t); void onFailed(Exception e); }

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

> callback);

    /**

     * 约定最后一个參数是callback

     */

}
" data-snippet-id="ext.f115dcec337f47ddaaf65522ab40a2e0" data-snippet-saved="false" data-csrftoken="hOCzSeOZ-2RNrlcsbRh9aW4trRlUk_l6C1D4">
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的源代码。

Android开发中无处不在的设计模式——动态代理模式的更多相关文章

  1. Android开发中常见的设计模式 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Android开发中常见的设计模式

    对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的 ...

  3. Android开发中常用的设计模式

    首先需要说明的是,这篇博文灵感来自于 http://www.cnblogs.com/qianxudetianxia/archive/2011/07/29/2121547.html ,在这里,博主已经很 ...

  4. Android开发中常见的设计模式(二)——Builder模式

    了解了单例模式,接下来介绍另一个常见的模式--Builder模式. 那么什么是Builder模式呢.通过搜索,会发现大部分网上的定义都是 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建 ...

  5. Android开发中常见的设计模式(一)——单例模式

    首先了解一些单例模式的概念. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这样做有以下几个优点 对于那些比较耗内存的类,只实例化一次可以大大提高性能,尤其是在移动开发中. 保持 ...

  6. android开发中使用到的一些设计者模式

    单例模式 概念:确保一个类只有一个实例,并且自行实例化并向整个系统提供整个实例. public class Singleton { private static volatile Singleton ...

  7. Android开发中常见的设计模式(四)——策略模式

    策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变换. 假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等 ...

  8. Android开发中常见的设计模式(三)——观察者模式

    先看下这个模式的定义. 定义对象间的一种一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都能得到通知并被自动更新 先来讲几个情景. 情景1:有一种短信服务,比如天气预报服务,一旦你订阅 ...

  9. Java开发中常用的设计模式(一)---工厂模式

    一. 准备工作 1. 本文参考自  自己理解的工厂模式,希望对大家有所帮助 二. 开始 以汽车工厂为例,首先有个汽车类的接口 Car,里面有个开车的方法 drive(),然后有个宝马车的类 BMW 和 ...

随机推荐

  1. Xcode7安装CocoaPods

    一.CocoaPods介绍以及优点 CocoaPods 是开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具.该项目源代码在Github上管理. 通过 CocoaPods,能够非常方便的 ...

  2. javascript用正則表達式检測username的合法性

    在用户登录.用户注冊时经常须要对username如邮箱.手机号进行校验,一般经常使用表达式. 以下整理对邮箱和手机号的表达式校验: function checkUser(){ var userName ...

  3. logback-spring.xml配置示例

    <?xml version="1.0" encoding="UTF-8"?> <configuration> <include r ...

  4. sshd服务分析

    00.阐述 sshd 服务 基本上,所有的 ssh 相关设定都放在 /etc/ssh/sshd_config 里面!不过,每个 Linux distribution 的预设设定都不太相同, 所以我们有 ...

  5. springboot自定义jdbc操作库+基于注解切点AOP

    发布时间:2018-11-08   技术:springboot+aop   概述 springBoot集成了自定义的jdbc操作类及AOP,因为spring自带的JdbcTemplate在实际项目中并 ...

  6. MFQ

    一什么是MFQ&PPDCS?MFQ&PPDCS是由外部教练邰晓梅提出的一套测试分析和测试设计方法.MFQ将被测对象分层,针对不同层次进行测试分析和设计进行,使测试设计人员不会那么容易忘 ...

  7. win32下编译glog

    既然编译第三方库了,google提供的VSproject是老版本的,构建不好升级.所以还是用cmake是王道. 采用out of source 编译,  以下是编译脚本bat: mkdir build ...

  8. JavaScript 风格指南

    来源于: https://github.com/alivebao/clean-code-js 目录 介绍 变量 函数 对象和数据结构 类 测试 并发 错误处理 格式化 注释 介绍 作者根据 Rober ...

  9. ASP.NET MVC3-第02节-添加一个Controller (C#)

    前言 ---------------------------- 第01节[翻译]01-ASP.NET MVC 3介绍 ---------------------------- MVC是“model-v ...

  10. LNMP分离式部署实例[转]

    很多人在练习部署LNMP环境的时候,大都数是部署在同一个虚拟机上面的.但是实际工作中,我们一般都是分离部署的. 今天我就用3台虚拟机,部署下LNMP环境.以供参考! 网络拓扑图: 首先准备3台虚拟机: ...