阅读此篇博客,你需要备Java动态代理的知识、反射和注解相关的知识,不然你阅读可能有些困难
在使用Retrofit的时候,我们经常通常看到有一个用于请求的接口,然后使用
Api api= retrofit.create(Api.class);
这样子创建了以后,这个接口Api里面的方法就能正常使用了,博主之前也是很好奇,之前误以为是,这里创建的时候是帮我实现了接口中所有的方法,然而今天学习了动态代理之后才知道并非如此,如果大家没有动态代理的知识储备,建议先去了解一下,不然下面可能你看不大懂
我稍微总结一下Java的动态代理:
这是一种设计模式,我个人理解,就是一个需要被代理的类A,如果A是一个接口,那么会有一个实现类B,创建代理对象C,这时候,你想调用A中的方法,就可以调用C中的方法,所有方法的调用都会经过InvocationHandler接口中的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 返回方法的结果
return null;
}
从此方法中可以看出有
1.要执行的方法:method
2.方法要执行的参数:args
3.还有一个代理类对象:proxy(这个对象就是上述举例中的C对象)
4.还有一个返回值,表示调用此方法的返回值
所以有了这些参数,你就完全可以控制每一个方法的执行,和每一个方法返回的结果了,这里唯一缺少的就是此方法执行的对象,也就是缺少上述中例子中的A(如果A是正常的类)或者B(如果A是接口)
但是我们今天讨论的不是正常的一个Aop思想的代理。Aop就是在上述invoke拦截方法中调用真正的对象A的过程,调用之前和之后你都可以做一定的操作,这里不再细说
今天要讨论的是A类是一个接口,我们并没有写实现类,为什么Retrofit框架能执行里面的方法并给我们想要的请求结果呢
比如这个名称为Api的接口,里面有一个方法
/**
* 接口
*
* @author cxj
*
*/
public interface Api {
/**
*
* @param name
* 用户名
*/
@Post("/postTest")
public String postTest(String name);
}
然后你在此方法上描述了你的请求,比如是Post的请求,这里是为了让大家回忆起来
@Post注解也是博主造假的
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Post {
// 请求地址
String value();
}
然后我们有这么一步
// 创建接口实现类,代理对象搞定
Api api = Retrofit.create(Api.class);
然后我们就可以调用api中的方法啦,我们上述讲到,如果使用代理来实现,此处的Api接口就是上述例子中的A(是一个接口)
然后返回的代理对象由于是Api的实现类,所以直接可以赋值给Api(多态)
如果这时候我们调用api中的方法
全部代码:
public static void main(String[] args) {
// 创建接口实现类,代理对象搞定
Api api = Retrofit.create(Api.class);
System.out.println(api.getClass().getName());
String result = api.postTest("cxj");
System.out.println("result = " + result);
}
我们上述说了,代理类的所有方法都会执行代理拦截类中的invoke方法
这是我造假的Retrofit对象
public class Retrofit {
/**
* 创建代理对象
*
* @param service
* @return
*/
@SuppressWarnings("unchecked")
public static T create(Class service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new MInvocationHandler());
}
static class MInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 返回方法的结果
return "hello";
}
}
}
Retrofit类中创建代理对象的方法可以去查阅其他资料或者博文,这里不讲。
创建的时候我们可以看到,第三个参数是new MInvocationHandler();
即下面这个静态的内部类,就是代理类拦截接口的实现类,所以这里面的invoke方法在调用
String result = api.postTest("cxj");
这句代码的时候会被执行,而我呢啥事情也没干,直接返回了一个hello
看运行结果
你会发现方法返回了hello
那我们在Api中添加一个方法呢
然后我们在main方法中调用此方法
你会发现同样是返回了hello.
所以你要彻底明白了一个事情,那就是网络框架Retrofit在实现你的接口中的方法的时候,并没有真正的去调用某一个对象的某一个方法.而是在invoke方法中读取了你调用的方法的
1请求地址
2.请求方式
3请求参数
……..
然后返回你一个Call对象,Call里面调用enqueue方法才是真正的发出了网络请求,这个网络请求的各个信息就是在invoke方法中读取然后封装的
再加工一下我们的代码,假如我想读取我调用方法是什么方法,并且访问的路径是什么,那我们是不是也应该在invoke方法中做手脚呢?答案是明显的
改造后的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断方法上是否有Post注解
boolean b = method.isAnnotationPresent(Post.class);
if (b) {// 如果有
// 拿到注解
Post post = method.getAnnotation(Post.class);
// 拿到访问的路径
String url = post.value();
System.out.println("是一个Post请求,并且返回的路径是:" + url + "\n");
}
// 判断方法上是否有Get注解
b = method.isAnnotationPresent(Get.class);
if (b) {// 如果有
// 拿到注解
Get get = method.getAnnotation(Get.class);
// 拿到访问的路径
String url = get.value();
System.out.println("是一个Get请求,并且返回的路径是:" + url + "\n");
}
// 返回方法的结果
return "hello";
}
需要你有反射和注解之类的知识!
你可以看到我获取了方法上的注解,然后获取了注解里面的属性,这不就是Retrofit框架做的事情么,拿到了你方法上的各个注解和参数中的注解,然后进一步拿到数据
运行结果:
本篇博客只是点了一下Retrofit的代理实现,没有那么详细的去讲更多的细节,但是已经点出了最核心的一点,也是博主之前最有疑问的一个地方.