@Override
public void buyFruit() {
// 到超市买水果
}
}
那么最终的实现效果就是,我们通过外卖App的一顿操作,从超市买到了水果,如下:
public class Main {
public static void main(String[] args) {
// 委托对象,超市;
Market market = new Market();
// 代理对象,外卖App;
AppAgent appAgent = new AppAgent(market);
// 通过外卖App的代理,从超市买到了水果;
appAgent.buyFruit();
}
}
以上就是我们静态代理的过程,这个例子只是举了买水果这个过程,但是如果我还想要买菜,买生活用品等一系列东西呢?
我就得在接口Operator里面再多新增好几个方法,同样的代理类也要跟着去重写一堆的方法,但是这些方法做的操作其实都是一样的,都是买这个动作,但是我们不得已,新增一种类型,我们就得在代理类里面再重写并调用;
那么这个过程其实是可以抽出来的,这种方式就叫做动态代理;
5.3、动态代理
动态代理,和静态代理不同的是,动态代理的方法是运行后才创建的,而静态代理是运行前就存在的了;
说白了,和静态代理不同的是,动态代理的方法都是在运行后,自动生成的,所以叫动态代理;
下面我们来看看动态代理是咋用的;
在使用动态代理的时候,被代理类需要实现InvocationHandler这个接口,,这个invoke方法是在动态生成的代理类中调用的,对应着我们上面在静态代理operator.buyFruit()这个方法的调用,下面来看一下这个方法对应的参数;
public interface InvocationHandler {
// Object proxy:接口的具体实现类;
// Method method:解析之后自动生成的方法;
// Object[] args:方法对于的参数;
Object invoke(Object proxy, Method method, Object[] args);
}
而最终运行时生成的代理类,一般名称会是Proxy1这种,通过Proxy.newProxyInstance()方法来生成的,这个下面会讲到,先来看一下下面的伪代码;
public final class $Proxy0 extends Proxy implements Operator {
public final boolean buyFruit() {
// h是InvocationHandlel,调用了invoke方法;
super.h.invoke(this, m1, (Object[])null);
}
}
}
在生成的代理类中,会实现我们的接口,并重写方法,在方法里面通过InvocationHandler回调参数到invoke方法里,最终通过反射调用被代理对象的方法;
而我们通过实现这个InvocationHandler接口,在invoke方法里面,通过method.invoke(object, args)可以来调用被代理的方法,然后我们可以在这个method.invoke(object, args)之前或者之后做一些处理,这样所以的方法都可以一并进行处理,而不是每次新增一个方法,就得重写一遍逻辑;
下面来看一下具体实现:
public class CustomProxy implements InvocationHandler {
private Object object;
public CustomProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1、在App上,提供商品给用户下单
// doSomeThing();
// 2、根据订单去超市采购东西(在这个方法之前或者之后都可以统一处理操作)
Object invoke = method.invoke(object, args);
// 3、送货给客户
// doSomeThing();
return invoke;
}
}
下面来看一下,最终的调用;
public class Main {
public static void main(String[] args) {
// 创建被代理类
CustomProxy customProxy = new CustomProxy(new Market());
// 动态生成代理类
Operator proxyInstance = (Operator) Proxy.newProxyInstance(Operator.class.getClassLoader(), new Class[]{Operator.class}, customProxy);
// 调用对应的方法
proxyInstance.buyFruit();
}
}
这里通过Proxy.newProxyInstance()方法,动态生成代理类$Proxy0这种,这里涉及到的反射的知识,就不再赘述了;
然后动态代理类调用方法,最终会走到CustomProxy的invoke()方法,然后我们在这个方法里面通过method.invoke(object, args)来进行最终的代理调用,而在这个最终的代理调用的前后,我们可以实现自定义的逻辑;
这个实现了InvocationHandler接口的CustomProxy,更像是一个拦截类,在代理方法的调用过程中进行拦截,然后再实现我们的自定义逻辑;
至此,动态代理你理解了吗?没有理解也没关系,多看几遍,多练几遍就可以了;
6、Retrofit的动态代理
6.1、Retrofit为什么要使用动态代理?
首先,让我们来想一个问题,Retrofit为什么要使用动态代理?
使用动态代理的好处就是在调用方法之前,我们可以统一做一些操作,而不必新增一个方法就去写一遍逻辑;
而Retrofit巧妙的使用了动态代理在调用接口的方法之前,统一的去解析处理Header和URL等操作;
这样就不用每次在新增一个请求的方法,就去写一遍这个解析的逻辑;
那么接下来我们来看看Retrofit的怎么解析这些接口的;
6.2、Retrofit是怎么使用动态代理的?
下面我们来看一下Retrofit的create的方法,动态代理的逻辑是在这里实现的;
public T create(final Class service) {
…
// 验证接口的参数以及配置是否正确
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 动态代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 判断是否是Object,如果是的话,就直接调用方法返回
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 判断是否是Java8平台的默认方法类型,如果是的话,就调用Java8平台的invokeDefaultMethod方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 解析方法;
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
这里我们将这个方法分为两步;
第一步: eagerlyValidateMethods方法,这个方法的逻辑是用于加载接口的配置,用于判断接口对应的header,body以及方法的参数等配置是否正确,如果不正确那么就会抛出异常;
第二步: loadServiceMethod方法,这个方法的逻辑主要是用于解析我们在接口配置的注解以及参数,比如header,body,url等等;
这里我们重点关注第二步的loadServiceMethod方法方法;
我们来看一下其源码的具体实现;
ServiceMethod<?> loadServiceMethod(Method method) {
// 先从缓存map集合里面获取ServiceMethod;
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 如果从缓存map里面获取不到ServiceMethod,那么再通过解析注解,获取到ServiceMethod对象;
result = ServiceMethod.parseAnnotations(this, method);
// 将解析后的ServiceMethod对象存入到map集合中;
serviceMethodCache.put(method, result);
}
}
return result;
}
这里做的操作很简单,就是获取ServiceMethod,而在获取ServiceMethod的过程中,会先从缓存的map中获取,如果获取不到了再进行解析,这样就不必获取一次ServiceMethod,就去解析一次,比较耗性能;
而这个ServiceMethod的类是个抽象类,只有两个方法,一个是静态的parseAnnotations方法,一个是抽象的invoke方法;
我们先来看一下这个parseAnnotations方法;
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
// 通过解析接口方法的注解,获取RequestFactory
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
…
// 解析注解并获取ServiceMethod对象
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
这个方法的逻辑也不是很复杂,主要分为两步;
第一步: 获取RequestFactory;
第二步: 获取ServiceMethod;
我们先来看第一步的操作;
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
通过Builder来创建RequestFactory,来看看这个Builder做了啥操作;
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 获取方法所有的注解,包括自己声明的以及继承的
this.methodAnnotations = method.getAnnotations();
// 获取方法参数的所有类型,包含泛型;
this.parameterTypes = method.getGenericParameterTypes();
// 获取方法参数上的所有注解
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
这个方法的逻辑很简单,就是做一些赋值操作,这里需要注意的是这几个反射的方法,下面的build方法会用到;
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
// 遍历方法的注解,解析方法的参数配置,获取到请求的url,header等参数
parseMethodAnnotation(annotation);
}
…
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
// 遍历方法的参数,以及参数的类型,解析方法的参数逻辑
parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
}
…
// 根据上面解析的参数配置,创建RequestFactory
return new RequestFactory(this);
}
这个方法的逻辑就比较重要了,我们在接口的方法里面定义的相关url,header等注解,最终就是在这里解析并转化为okhttp请求的Call,那么我们来看看这里到底是怎么解析的;
先来看一下parseMethodAnnotation这个方法, 这个方法的主要逻辑是用于解析方法注解的配置信息;
下面我们来看看这个类的具体实现;
这个方法的逻辑比较多,我们大致看一下就可以了,这里面做的主要职责就是通过注解Annotation,获取到url,header,isMultipart等参数,并将其赋值给建造者Builder的成员变量;
对于这个方法,这里就不多说了,感兴趣的跟着源码去看一下;
而第二个方法parseParameter,也是遍历上面获取到的方法的参数类型parameterTypes以及方法参数的注解parameterAnnotationsArray,来解析并获取相关配置,而这个方法最终是调的parseParameterAnnotation方法的逻辑;
主要是用于解析这里的逻辑:
下面我们来看看具体实现;
这个parseParameterAnnotation方法的逻辑和上面的parseMethodAnnotation方法有点类似,也是通过判断对于的类型来进行解析,这里源码过长,就不贴出来了;
这里我们就找其中一个Query的解析来进行分析;
省略前面的代码
…
else if (annotation instanceof Query) {
…
// 将注解annotation强转为Query
Query query = (Query) annotation;
// 获取注解Query对应的值;
String name = query.value();
…
if (Iterable.class.isAssignableFrom(rawParameterType)) {
// 判断这个参数的类型为Class类型;
…
// 创建ParameterHandler的子类Query
return new ParameterHandler.Query<>(name, converter, encoded).iterable();
}
…
}
…
省略后面的代码
这里最终解析获取的是ParameterHandler.Query类,这个类里面有个apply的方法,会在接口请求的时候会调用到,目的是通过请求的RequestBuilder将解析出来的参数,添加到RequestBuilder里去;
这里我们了解一下就可以了;
这里还有一个需要注意的点,就是这里的解析,涉及到Converter这个convert方法的逻辑;
这里我以Body为例,在Body这个类的apply方法里,会通过Converter这个convert方法,将body参数转化为对应类型的数据;
这个apply的方法是在进行网络请求的时候会调用,具体调用链如下,这里以RxJava为例:
1、RxJavaCallAdapterFactory.ResponseCallAdapter的adapt方法;
2、RxJavaCallAdapterFactory.CallOnSubscribe的call方法;
3、RxJavaCallAdapterFactory.RequestArbiter的request方法;
4、OkHttpCall的execute方法;
5、OkHttpCall的createRawCall方法;
6、RequestFactory的create方法;
7、ParameterHandler的apply方法;
最终在这里触发了apply方法的调用,这里源码就不贴出来了,感兴趣的朋友,可以跟着源码去看一遍;
我们上面看的RequestFactory的build方法的最后一个,创建RequestFactory,这里的逻辑就是将我们上面解析出来的参数,给这个RequestFactory进行赋值;
那么到这里parseAnnotations的第一步就讲完了,先来总结一下,这一步主要是解析方法注解和方法参数的注解,从而获取到对应的参数配置,最终将其赋值给RequestFactory对象;
下面我们来看HttpServiceMethod的parseAnnotations方法;
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 创建CallAdapter
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
…
// 创建Converter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
// 通过参数创建HttpServiceMethod
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
这个方法的逻辑并不复杂,主要分三步:
第一步: 创建CallAdapter;
第二步: 创建Converter;
第三步: 通过参数创建HttpServiceMethod;
这里主要关注的是前两步,创建了CallAdapter和Converter;
我们先来看一下第一步的逻辑;
这里的调用链是这样的,首先调用了HttpServiceMethod的createCallAdapter方法,然后再调用了Retrofit的callAdapter方法,最后调用了Retrofit的nextCallAdapter方法;
我们来看看nextCallAdapter的逻辑;
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
…
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。
小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!
不论遇到什么困难,都不应该成为我们放弃的理由!
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-phbhKdpJ-1712522666621)]
最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。
小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!
不论遇到什么困难,都不应该成为我们放弃的理由!
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算