动态代理及原理思考

动态代理基础

动态代理是在程序运行时动态创建一个代理类,实现的过程和静态代理一致,只是它是由反射实现的, 是AOP的基础

动态代理类不会继承被代理类的接口,而是实现InvocationHandler ,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class OwnerInvocationHandler implements InvocationHandler {
private PersonBean personBean;

OwnerInvocationHandler(PersonBean personBean){
this.personBean = personBean;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
if(method.getName().startsWith("get")){
return method.invoke(personBean,args);
}else if(method.getName().equals("setHotOrNotRating")){
throw new IllegalAccessException();
}else if(method.getName().startsWith("set")){
return method.invoke(personBean,args);
}
}catch (InvalidParameterException e){
e.printStackTrace();
}
return null;
}
}

我们关注的就只是invoke方法和被传入的被代理对象personBean

创建代理类实例

1
2
3
4
PersonBean person = new PersonBeanImpl();
(PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person))

可以看到代理类是用反射实现的
当然,也可以把创建过程写到动态代理类中

1
2
3
4
5
public static PersonBean newProxyInstance(PersonBean  person){
return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person))
}

Retrofit中动态类的实现

撇开动态代理模式不谈,可以看到,Proxy可以生成生成一个继承特定接口的类,而一个继承了该接口的实现类也不是必要的。

这时联想到Retrofit,它也是使用Proxy生成的

1
2
3
4
5
6
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
1
2
3
4
5
6
7
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();

// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);

retrofit.create的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();

@Override public Object invoke(Object proxy, Method method, 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);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

通过retrofit.create创建的对象,在执行相应的方法时都会经过这个内部类的invoke方法,再通过ServiceMethod 进行相关的请求。
其实仔细想就明白了,每个网络请求其实并不关心叫什么名子,它只需要知道请求的url和参数就可以接着进行统一的网络请求就可以,所以看起来我们在定义接口的时候每个请求都定义了一个方法,但实际上这些方法没必要每个都实现。
而这个ServiceMethod 内部不用看就知道肯定是会通过method来读取每个接口上的注解,以便知道该进行什么样的网络请求

动态代理类实现原理分析:
可以看到,通过Proxy.newProxyInstance生成了一个继承通用接口的实例,调用它的方法时就会调用动态代理类InvocationHandlerinvoke方法,那么这个类是实现的呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
//这里生成了一个继承通用接口的类
Class<?> cl = getProxyClass0(loader, intfs);
try {
//可以发现这个类也有一个构造方法是传入InvocationHandler的
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
// Android-changed: Removed AccessController.doPrivileged
cons.setAccessible(true);
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

通过cl.getConstructor(constructorParams)可以看到创建的动态类是有构造方法是传入了InvocationHandler
生成这个类是应该是在getProxyClass0里面

1
2
3
4
5
6
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
return proxyClassCache.get(loader, interfaces);
}

proxyClassCache很明显只是一个缓存用的对象,正直生成的地方应该就是ProxyClassFactory
这个ProxyClassFactory内部收集完必要的信息后调用一个native方法生成并加载到内存的
根据网上的资料,可以通过以下方法把这个类给弄出来

1
2
3
4
5
6
7
8
9
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", PersonBean.class.getInterfaces());
String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}