动态代理和静态代理的区别之一就是可以同时代理同一个对象的多个接口
public interface Husband {
void careFamily();
}
public interface Father {
void teachChild();
}
Man man = new Man();
Object o = Proxy.newProxyInstance(Man.class.getClassLoader(), new Class[]{Husband.class, Father.class}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return method.invoke(man, objects);
}
});
((Husband)o).careFamily();
((Father)o).teachChild();
简单看下源码
可以看到,返回值是getProxyClass0方法得到的Class后,通过反射创建了这个Class的对象,那么getProxyClass0做了什么?
public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {
......
Class var5 = getProxyClass0(var0, var3);
try {
......
final Constructor var6 = var5.getConstructor(constructorParams);
......
return var6.newInstance(var2);
} catch (Exception var10) {
......
}
}
可以看到是从一个WeakCache<ClassLoader, Class<?>[], Class<?>>取出来的,说明已经生成好了,生成的时机我们不去考虑
private static Class<?> getProxyClass0(ClassLoader var0, Class... var1) {
if (var1.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
} else {
return (Class)proxyClassCache.get(var0, var1);
}
}
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache(new Proxy.KeyFactory(), new Proxy.ProxyClassFactory());
关键就是在ProxyClassFactory类中
Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译
而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。
在Proxy中有一个静态内部类:ProxyClassFactory,在他的apply方法中可以看到这样一行代码
String var23 = var16 + "$Proxy" + var19;
//第一参数表示类名,是拼装后的,第二个表示 Class<?>[] var1,代理接口数组
byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
通过这个方法会在内存中生成一个类的字节码数组,然后
Proxy.defineClass0(var1, var23, var22, 0, var22.length);
定义出这个类,内存中就有这个类了
模拟生成这个类
我们按照这种方式,将生成类写入文件可以看到类长什么样子
String name = Husband.class.getName() + "$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Husband.class});
FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
fos.write(bytes);
fos.close();
生成的类如下,可以看到,这个类继承自Proxy,实现了我们需要代理的接口,继承自Proxy,从前边的分析我们可以知道,Proxy.newProxyInstance返回的就是这个类的对象,所以他可以强转成我们的代理接口,因为实现自这个接口
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.enjoy.lib;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class Husband$Proxy0 extends Proxy implements Husband {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//可以看到静态代码块中生成了四个方法,其中有三个equals toString hashCode是每个类自带的(Object)
//careFamily是我们接口中提供的方法
//类一生成就通过反射获取到了几个关键方法的method
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.enjoy.lib.Husband").getMethod("careFamily");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public Husband$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void careFamily() throws {
try {
//当外界调用careFamilly方法的时候,会执行到这里,h就是传入的InvocationHandler
//m3就是静态代码块中获取到的careFamily方法(来自接口),因为我们方法没有传参数,所以为null
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
所以看到这里我们大概可以知道动态代理的原理了,动态代理通过传入的代理接口在内存中生成一个代理对象,这个代理对象实现了我们传入接口的方法,如此一来,外部在拿到代理对象的时候就可以强转成对应的接口,调用相应的方法,调用方法后会执行代理对象中对应的方法,然后通过InvocationHandler的invoke方法将对应接口的对应方法的method和参数回调出来,然后再次使用反射,传入我们的实例对象,这样便达到了调用我们实例对象方法的目的
所以想象中高大上的动态代理不过如此!
Retrofit中的泛型 注解 和动态代理
我们只需要知道,Retrofit是对OkHttp的一层封装,本身他没有做网络请求的功能,他只是一层封装
使用Retrofit是从创建一个接口开始的
public interface WeatherApi {
@POST("/v3/weather/weatherInfo")
Call postWeather(@Field("city") String city, @Field("key") String key);
@GET("/v3/weather/weatherInfo")
Call getWeather(@Query("city") String city, @Query("key") String key);
}
在调用的时候
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://restapi.amap.com")
.build();
weatherApi = retrofit.create(WeatherApi.class);
Call<ResponseBody> call = weatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new Callback<ResponseBody>() {...}
核心是在retrofit.create(WeatherApi.class)方法上,传入我们上边实现的接口class,拿到代理对象,没错,就是用到了动态代理
public <T> T create(final Class<T> service) {
validateServiceInterface(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 @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
于是等通过代理对象调用getWeather方法的时候,就会进入到动态代理的invoke方法,在这里会进行一系列的处理,包括解析接口上的配置信息,解析接口方法的参数信息,最后拼成一个url,最后执行反射,拿到一个Okhttp的Call,就可以执行请求了
call.enqueue(new Callback<ResponseBody>() {...}
网友评论