因为Retrofit剖析源码的时候会用到ava中的代理模式,所以这篇就先回忆一下代理设计模式。
代理模式分为两种:
代理模式解释:为其他对象提供一种代理,用以控制对这个对象的访问。(直白一点就是代替别人去做一些我们想要做的事情)
1.静态代理(抽象类)
代码实现:
/**
* 被目标对象和代理对象同时继承的抽象类
*/
public abstract class AbstractObject {
protected abstract void operation(); // 经营预算
}
被代理对象:
/**
* 目标对象 (被代理对象)
*/
public class RealObject extends AbstractObject{
private static final String TAG = RealObject.class.getSimpleName();
@Override
public void operation() {
Log.e(TAG,"do operation......");
}
}
代理对象:
/**
* 代理类
*/
public class ProxyObject extends AbstractObject {
private static final String TAG = ProxyObject.class.getSimpleName();
// 对目标类的引用
private RealObject mRealObject;
public ProxyObject(RealObject mRealObject) {
this.mRealObject = mRealObject;
}
@Override
public void operation() {
Log.e(TAG,"do something before real operation......");
if (mRealObject == null) {
mRealObject = new RealObject();
}
mRealObject.operation();
Log.e(TAG,"do something after real operation......");
}
}
测试:
public static void main(String[] args) {
ProxyObject proxyObject = new ProxyObject(new RealObject());
proxyObject.operation();
}
do something before real operation......
do operation......
do something after real operation......
2.动态代理(JDK实现/CJLib继承)
- 概念解释:代理类在程序运行时创建的代理方式。
- 相比于静态代理:动态代理可以很方便的去改变代理类的函数进行统一的处理,而不用去修改每一个代理类的函数,根据你相应的业务逻辑
- 优点:无侵入式的扩展代码
JDK动态代理:Java内部的反射机制来实现的,这个机制在生成类的时候比较高效。(必须要针对接口进行代理)
在不改变原来代码的情况下,在调用方法的前后,插入我们自己想要的逻辑。(例子)
动态代理和静态代理最大的不同,动态代理的代理类是不需要手动生成的,是根据我们的配置在运行时动态生成的。(是在运行时生成的代理)
需要用到InvocationHandler接口和Proxy类
代码示例:
被目标对象和代理对象都要实现的接口
/**
* 动态代理
* 被目标对象和代理对象都要实现的接口
*/
public interface InterfaceObject {
void shopping();
}
被代理类 (目标类):
/**
* 动态代理
* 被代理类 (目标类)
*/
public class BeRepresent implements InterfaceObject {
private static final String TAG = BeRepresent.class.getSimpleName();
@Override
public void shopping() {
System.out.println("买点东西回来");
}
}
代理类是由JDK动态创建的:
/**
* 动态代理
* 代理类:每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现
* 当使用者调用了代理对象,所实现的代理对象的接口时,这个调用的信息就会传递到InvocationHandler
* 中的invoke方法中
*/
public class ProxyF implements InvocationHandler {
private static final String TAG = ProxyF.class.getSimpleName();
private Object target; // 要代理的真实对象
public ProxyF(Object target) {
this.target = target;
}
/**
* 相当于一个拦截的方法
* @param proxy 代理对象
* @param method 代理方法
* @param objects 代理方法中的参数
* @return 返回值会返回给使用者
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("代理对象:" + proxy.getClass().getName());
System.out.println("代理方法中的参数:" + (objects != null && objects.length > 0 ? objects[0] : null));
System.out.println("代理方法:" + method);
System.out.println(" before ");
method.invoke(target,objects);
System.out.println(" after ");
}
}
测试:
public static void main(String[] args) {
InterfaceObject mBeRepresent = new BeRepresent();
ProxyF proxy = new ProxyF(mBeRepresent);
// java.lang.reflect.Proxy.newProxyInstance(....)方法获得真实对象的代理对象 (反射)
InterfaceObject mInterfaceObject = (InterfaceObject) Proxy.newProxyInstance(mBeRepresent.getClass().getClassLoader()
, mBeRepresent.getClass().getInterfaces(), proxy);
// 通过代理对象调用真实对象相关接口中实现的方法,这个时候就会跳转到这个代理对象所关联的InvocationHandler
// 的invoke方法中
mInterfaceObject.shopping();
// 获得真实对象的代理对象所对应的class对象的名称,用字符串表示
System.out.println(mInterfaceObject.getClass().getName());
}
结果:
代理对象:com.sun.proxy.$Proxy0
代理方法中的参数:null
代理方法:public abstract void dynamicproxy.InterfaceObject.shopping()
before
买点东西回来
after
com.sun.proxy.$Proxy0
注意:被代理对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。
总结:
JDK实现原理:
1.拿到被代理对象的引用,然后获取它实现的接口
2.JDK重新生成一个类,同时实现获取到被代理对象所实现的接口
3.在代理类的静态构造块中,代理类通过反射获取了被代理类的详细信息
4.重新生成一个class字节码
5.然后编译
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
还有一种是CGlib实现的动态代理:
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
最后,抛出两个问题:
1.retrofit为什么要使用动态代理 ?
答案:
因为1个静态代理只服务1种类型的目标对象,若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象 ,在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂的问题,而动态代理只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码 更强的灵活性设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由JVM来实现,在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化。
2.动态代理的缺点是什么?
答案:
效率低
相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法。
应用场景局限
因为Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类即只能动态代理 实现了接口的类。