Retrofit与动态代理
什么是代理
代理,即Proxy
。它的作用就是将原数据与后续的操作进行隔离,达到对修改封闭,对扩展开放的效果。
从现实生活中理解,厂商生产产品到代理商,而代理商负责找渠道销售产品。期间,厂商与销售人员不会有任何接触,也不管销售效果如何。
什么是静态代理
在Java中,通常会用代理模式来完成一些额外的操作。
例如,有一件商品,售价为25元,但是经过代理商之后,售价要提高5元。
// 定义货物的接口,以及获取价格的函数
public interface Cargo{
public int getPrice();
}
// 定义产品,实现Cargo接口,返回25元
public class Product implements Cargo{
@Override
public int getPrice(){
return 25;
}
}
// 定义产品的代理,实现Cargo接口,并且在原有的基础上增加5元再进行销售
public class ProductProxy implements Cargo{
private Product product;
public ProductProxy(Product product){
this.product = product;
}
@Override
public int getPrice(){
return product.getPrice()+5;
}
}
静态代理的优点
- 隐藏委托类的实现,保证委托类的独立
- 实现代理与委托类之间的解耦,不侵入委托类的代码
动态代理的背景
在使用静态代理的过程中,会产生如下问题:
- 大型项目的复杂度,如果代理过多的话,会导致维护成本很大,并且难以理解
- 通过接口实现的静态代理作用死板,对于功能的复用有很大影响
举例,我们希望统计产品在生产过程中(原材料采购 --> 材料加工 --> 产品制作 --> 后期包装等步骤)消耗的时间,然后产生报表。如果还使用静态代理的话,则这个代理类中都是相同的代码,并且后续再添加接口,仍然需要修改该代理类
于是,Java提出了动态代理的概念。
动态代理
动态代理,也就是在运行时创建的代理类。在运行过程中,会在虚拟机内部创建一个Proxy的类。通过实现InvocationHandler
的接口,来代理委托类的函数调用。
public class ProductProxy implements InvocationHandler{
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
还是以上述商品来举例。
public class ProductProxy implements InvocationHandler{
// 被委托的对象
private Product product;
public ProductProxy(Product product){
this.product = product;
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("getPrice")){
// 如果调用的函数为getPrice的话,则+5
return method.invoke(product,args)+5;
}
return null;
}
}
public static void main(String []args){
// 创建代理类
ProductProxy proxy = new ProductProxy(new Product());
//获取代理类实例Product
Product product = (Product)(Proxy.newProxyInstance(Product.class.getClassLoader(), new Class[] {Product.class}, proxy));
// 调用getPrice的函数,就会走到动态代理的invoke函数中
product.getPrice();
}
通过动态代理,我们可以通过函数名来判断对应的函数以及对应操作,甚至于修改参数。
Retrofit中的动态代理
在Retrofit中,使用动态代理来对接口中的注释进行解析,解析后完成OkHttp的参数构建。
Retrofit的基本使用
首先来看一下使用Retrofit请求Github的的代码
- 通过
interface
以及注释定义了该为Get请求,并且路径为/
public interface GitHubService {
@GET("/")
Call<GitHubApiBean> listGitHubApis();
}
- 定义
baseUrl
为域名,并且添加GsonConverterFactory
作为Response的转换工厂,创建Retrofit对象
String url = "https://api.github.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)//url必须以‘/’结尾
.addConverterFactory(GsonConverterFactory.create())
.build();
- 调用函数,开始发起请求
//retrofit.create来生成一个接口实现类
GitHubService gitHubService=retrofit.create(GitHubService.class);
//调用指定方法
Call<GitHubApiBean> gitHubBeanCall=gitHubService.listGitHubApis();
//执行请求
gitHubBeanCall.enqueue(new Callback<GitHubApiBean>() {
@Override
public void onResponse(Call<GitHubApiBean> call, Response<GitHubApiBean> response) {
String authorizations_url= response.body().getAuthorizations_url();
String team_url= response.body().getTeam_url();
}
@Override
public void onFailure(Call<GitHubApiBean> call, Throwable t) {
Log.i("zfq", t.getMessage());
}
});
原理分析
在Retrofit的create
中会通过Proxy.newProxyInstance
来为传入的Service接口类创建一个代理对象,而当代理对象调用函数时,会调用动态代理的invoke
函数。
public <T> T create(final Class<T> service) {
...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
...
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
而在invoke
函数中,会通过调用loadServiceMethod
函数,对Method中的注释进行解析,并且返回ServiceMethod
对象,传入OkHttpCall
中,构建OkHttp的请求。
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
...
// 通过注释中的Converter,创建Response中的转换器
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
// 解析注释中的GET/Post、Multipart等等
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
...
// 解析函数参数中的注释,例如Query,PartMap,FieldMap等
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...
return new ServiceMethod<>(this);
}
Retrofit中的CallAdapter
CallAdapter
的作用则是将请求中的数据进行转换,通过adapt
函数进行转换,将A转换成B类型。
Retrofit.Builder test =new Retrofit.Builder().addCallAdapterFactory(new CallAdapter.Factory() {
@Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new CallAdapter<Object, Object>() {
@Override public Type responseType() {
return null;
}
@Override public Object adapt(Call<Object> call) {
return null;
}
};
}
});
- Java中使用Hibernate系列之映射关联启动工作学习(第五节)
- Java中使用Hibernate系列之单向Set-based的关联学习(第四节)
- Java中使用Hibernate系列之加载并存储对象学习(第三节)
- Java中使用Hibernate系列之启动方法学习(第二节)
- Java中使用Hibernate系列之映射文件学习(第一节)
- Java中为图片添加水印效果的方法——实例代码
- Java中使用Hibernate系列之过滤器(filters)学习
- Node.js中的内存泄漏分析
- Java实现把整数转换为英语单词的方法,实用代码
- Chrome XSS审计之SVG标签绕过
- Java实现的一个简单计算器,有字符分析功能
- Java中实现判断括号是否有效的方法,实用代码
- Java中使用栈实现一个队列,实用代码
- NDK 的开发流程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- xtraReprot 动态绑定数据 数据列动态
- 线上问题分析之java dump文件生成
- python基础知识
- AtCoder Beginner Contest 177 A ~ E
- 2017 年ICPC 中国大陆区域赛铜牌题解
- 搜索(DFS BFS)专题练习
- AtCoder Beginner Contest 171
- AtCoder Beginner Contest 173 A ~ F(已经补完)
- AtCoder Beginner Contest 174 A ~ E
- AtCoder Beginner Contest 170
- 【队伍训练3】Codeforces Round #661 (Div. 3)
- 购物
- 指纹锁(自定义下比较重载下set的圆括号比较)
- 糖糖别胡说,我真的不是签到题目
- 分数线划定