前言
在看retrofit实现的时候,一个很大的亮点就是使用的动态代理模式,问别人说你看过retrofit?ennnn,看过呀,无非就一个代理模式嘛,ennnn,为啥要使用代理模式?我一脸懵逼。好像上次看到代理模式还是在学校,所以就重新干了一波。(每次看完设计模式总有牛逼呀老铁的觉悟)
代理模式
什么是代理模式?为其他对象提供一种代理以控制对这个对象的访问,简单来说,就是为你本来的类提供1个代理类,通过代理类来控制本来的对象。
好处?
1. 职责清晰: 真实的角色就是实现实际的业务逻辑,不需要关心其他,代理可以帮助完成其他事情。
2. 高扩展 :代理可以随意改变都不会影响本来的真实角色
3. 更加智能:动态代理会讲
静态代理模式
我们先看下静态代理模式。
上图:
我们来看下这3个角色的定义
Subject 是抽象的角色
RealSubject 是真正的业务实现类
Proxy 是代理类,它负责对真实对象的应用。
举个例子 出着设计模式之禅 是一个游戏者打怪升级的例子
有个一个 先上代码
1.我们定义了一个接口,这个就是我们的抽象角色。
//游戏者接口
public interface IGameplayer {
//登陆
public void login(String name, String password);
//杀boss
public void killboss();
//升级
public void upgrade();
}
- 我们实现了这个接口。这个就是我们的真正的业务实现类
public class Gameplayer implements IGameplayer {
private String name = "";
public Gameplayer(String name) {
this.name = name;
}
@Override
public void login(String name, String password) {
System.out.println(this.name+"登录");
}
@Override
public void killboss() {
System.out.println(this.name+"打怪");
}
@Override
public void upgrade() {
System.out.println(this.name+"升级");
}
}
- 我们在实现一个代理类,负责对真实对象的应用.
public class GamePalyProxy implements IGameplayer {
private IGameplayer gameplayer = null;
public GamePalyProxy(IGameplayer gameplayer) {
this.gameplayer = gameplayer;
}
@Override
public void login(String name, String password) {
gameplayer.login(name,password);
}
@Override
public void killboss() {
gameplayer.killboss();
}
@Override
public void upgrade() {
gameplayer.upgrade();
}
}
还有一个场景类,来调用代理类
public class Client {
public static void main(String[] args) {
Gameplayer gameplayer = new Gameplayer("Abner");
GamePalyProxy gamePalyProxy = new GamePalyProxy(gameplayer);
gamePalyProxy.login("abner", "123");
gamePalyProxy.killboss();
gamePalyProxy.upgrade();
}
}
结果:
Abner登录
Abner打怪
Abner升级
就得到了这个结果,看到这一步,可能有的同学会说,干嘛要这个代理类,我直接调用Gameplayer也可以实现呀,这样不是多此一举?ennnn 如果你可以从此以后都是这样子,保证不需要做多一点的操作,那确实这样做是多此一举,那如果我想在升级前做一些事情?或者想在升级后做一些事情?这样如果我们不用代理的话,那是不是我们就必须去重新修改我们的业务实现类。或者说我们修改了业务类,也会影响到我们高层的模块,如果这时候中间有个代理的,那么高层模块也不必关心实现类,实现类也不必关心高层类。但是代理做代理的事情,但是如果想代理另外一个游戏玩家?我们发现我们只能重新写一个逻辑了。然后我们代理口碑不错,来了无数个游戏玩家要代理,那就麻烦了,这时候静态代理已经走不动了 可是没关系,我们来讲下动态代理。
动态代理模式
动态代理才是我们今天的重头戏,retrofit里面用的也是动态代理,我们继续讲我们上面的那个例子。
先建一个handler类 重写invoke方法
public class GamePalyIH implements InvocationHandler{
//被代理者
Class cls= null;
//被代理的实例
Object obj = null;
public GamePalyIH(Object obj)
{
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj,args);
return result;
}
}
public class Client {
public static void main(String[] args) {
Gameplayer gameplayer = new Gameplayer("Abner");
// GamePalyProxy gamePalyProxy = new GamePalyProxy(gameplayer);
// gamePalyProxy.login("abner", "123");
// gamePalyProxy.killboss();
// gamePalyProxy.upgrade();
InvocationHandler handler = new GamePalyIH(gameplayer);
ClassLoader cl = gameplayer.getClass().getClassLoader();
IGameplayer proxy = (IGameplayer) Proxy.newProxyInstance(cl,new Class[]{IGameplayer.class},handler);
proxy.login("abner","123");
proxy.killboss();
proxy.upgrade();
}
}
打印的东西和我们刚才一模一样。神奇吧。这就是所谓的AOP编程,也叫面向切面编程,面向切面,我觉得已经很形象的说出这个东西了,刚才我们说了静态代理可以在实际业务类的执行前或者后去做一些操作,但是动态代理的神奇在于他动态,可以动态的指定一个实际业务类来得到一个代理对象。所以在设计的时候就完全可以不必去考虑,直接插入就可以用了。
Retrofit里面的使用
retrofit 在create的时候用了动态代理,返回了一个service的代理对象
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.callAdapter.adapt(okHttpCall);
}
});
}
所以retrofit 在create的时候 不用去考虑这是一个什么对象,但是可以在调用之前对这个对象进行一些处理,并且调用。像retrfit这个 在执行方法前做对方法的解析并且得到一个okHttpcall。
这样的设计真的很奇妙。