前言:做过Android开发的一般都使用过Retrofit这个三方框架,初一使用,会觉得很神奇:为什么把各个请求定义在一个接口里,通过几行代码就能实现网络请求?答案就是“动态代理”!
何为代理?
简单来说代理是一种软件设计模式,某些情况下,我们不希望修改已有对象的功能,但是我们又想在不改变调用者调用方法的情况下,改变调用后的具体实现逻辑,因此,我们会采用间接访问来实现目的。比如A类实现了IHelloWorld接口,有一个helloWorld方法,但在使用的时候并不直接调用a.helloWorld(),而是通过定一个同样实现了IHelloWorld接口的代理类B,在B的helloWorld方法中调用A的helloWorld方法,并夹带一些自己的私货。
//共同实现的接口
public interface IHelloWorld {
public void helloWorld(String str);
}
//真实对象
public class RealSubject implements IHelloWorld {
@Override
public void helloWorld() {
System.out.println("RealSubject say hello world");
}
}
//代理对象
public class Proxy implements IHelloWorld {
private IHelloWorld subject;
public Proxy(IHelloWorld subject) {
this.subject = subject;
}
@Override
public void helloWorld() {
//代理类的私货
System.out.println("Proxy say hello world");
//被代理对象的真实调用
subject.helloWorld();
}
}
//实际调用
public static void main(String[] args) {
//被代理的对象
RealSubject realSubject = new RealSubject();
//代理类对象
Proxy proxy = new Proxy(realSubject);
//调用代理类对象的方法
proxySubject.helloWorld();
}
一般的代理,就是这样的逻辑和实现。但是,如果如果需要代理的方法成百上千,总不能每一个方法都写一遍吧
动态代理
在java的动态代理机制中,有两个重要的类和接口:
- InvocationHandler(Interface)
- Proxy(Class)
-
InvocationHandler
每个代理实类例的invocation handler都要实现InvocationHandler,并且每个代理类的实例都关联到了一个handler,当通过代理对象调用一个方法的时候,这个调用就会被转发,由InvocationHandler这个接口的 invoke 方法来进行调用。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
//proxy:指代生成的代理对象;
//method:指代的是我们所要调用真实对象的某个方法的Method对象;
//args:指代的是调用真实对象某个方法时接受的参数;
-
Proxy
Proxy的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,其中核心方法就是 newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
我们通过一个具体的事例来理解:
public interface IHelloWorld {
public void helloWorld(String str);
}
public class RealSubject implements IHelloWorld {
@Override
public void helloWorld() {
System.out.println("RealSubject say hello world");
}
}
public class SubjectInvocationHandler implements InvocationHandler {
//这个就是我们要代理的真实对象
private Object subject;
//构造方法,给我们要代理的真实对象赋初值
public SubjectInvocationHandler(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
//动态代理的私货
System.out.println("invoke");
//被代理对象的方法调用
method.invoke(subject, args);
return null;
}
}
public static void main(String[] args) {
//被代理类
IHelloWorld realSubject = new RealSubject();
//我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
//生成代理类
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
//注意这里,后面会有讲解
System.out.println("Proxy : "+ subject.getClass().getName());
subject.helloWorld();
}
SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法的功能类,你可以理解为他帮助你实现了一个真正的代理类,我们在式例中打印Proxy的ClassName,得到的结果是:
Proxy : com.sun.proxy.$Proxy0
这个$Proxy0才是真正的代理类,由Proxy.newProxyInstance 创建,是在jvm运行时动态生成的一个对象,命名方式都是以$开头,proxy为中,最后一个数字表示对象的标号。
通过这样的方式,不再需要手动的一个个去实现代理方法:
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
//这里可以判断你想要代理的方法名
if (method.getName().equals("helloWorld")) {
//想加什么私货加什么私货
//被代理对象的真实调用
method.invoke(subject, args);
}
}
我们再来看一个Retrofit的例子
首先,按照常规的方式定义一个接口:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface NetProtocol {
@GET("/toutiao/index")
Call<JuheBaseBean<ToutiaoBean>> getNews(@Query("key") String key);
}
然后在代码中使用:
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://v.juhe.cn").addConverterFactory(GsonConverterFactory.create()).build();
NetProtocol protocol = retrofit.create(NetProtocol.class);
Call<JuheBaseBean<ToutiaoBean>> call = protocol.getNews("aadc832bcc8fec1602d547e3659714fd");
注意这里的 retrofit.create(NetProtocol.class)
我们来看下retrofit的creat方法的源代码:
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, @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);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
});
}
create()
方法中的范型T,就是我们的接口对象NetProtocol
,注意这里的return后面的代码,调用了Proxy.newProxyInstance,对照我们之前关于动态代理的示例,就很容易理解了。当NetProtocol
调用自身方法的时候,因为运用到了动态代理,所以会调用invoke
方法。