前段时间在研究插件化,其中主要看了任玉刚动态加载框架——dynamic-load-apk,其Activity的注册问题主要是通过静态代理的方式来完成,之前了解过代理模式,但具体没使用过,这次看到了具体的使用,于是更深入的去学习静态代理,以及动态代理。

本篇主要讲静态代理和JDK的动态代理的原理,也是Router——一种Android中使用的高效、方便的事件总线解耦库实现的主要原理。

代理模式是什么

由于一些原因,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。通过引入代理对象来间接访问一 个对象,这就是代理模式的模式动机。

定义:代理模式为另一个对象提供一个替身活占位符以控制这个对象的访问。

文字描述有些苍白,看下UMl图。

从UML图来看,代理模式和Decorator(装饰者模式)非常相似,有时很难区分。如果理解两者的差别主要在Decorator是对对象进行行为(方法)扩展,而代理主要是对对象的控制,这样就方便区分了。

Subject

Proxy和RealSubject都实现了SUbject接口,这允许客户都可以想使用RealSubject对象一样地处理proxy对象。

RealSubject

RealSubject通常是真正做事的对象,proxy会控制对RealSubject的访问。

Prxoy

Proxy持有RealSubject的引用,客户和RealSubject的交互必须通过Proxy。有些情况下Proxy控制了对RealSubject的访问,这些情况包括RealSubject是远程的对象、创建开销大或者需要安全保护。

动态代理

静态代理很简单,和上面UMl图表现出的一样,不清楚的Google一下,这里不再详细展开。主要是解读Jdk中提供的实现动态代理的api的原理与实际中的应用,也是Router最重要的部分。

Java在java.lang.reflect包中有代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法调用装发到你指定的类,因为实际的代理类是在运行时创建的,所以称之为动态代理。

这是使用Jdk中使用代理的方式,其最终仍可转化成最上面的那个UML所表示的情况。
InvacationHandler是包java.lang.reflect中提供的一个接口,用来帮助动态的创建代理对象。现在Proxy和你实现InvocationHandler接口的类整体上形成一个代理。Proxy上的任何方法都会被传入InvocationHandlerImpl,InvocationHandlerImpl控制着对RealSubject方法的访问。

现在看不明白这些没关系,结合下面的例子可以更好的理解。

首先声明一个共同的接口Subject:

1
2
3
interface Subject {
void request();
}

然后一个具体的实现类RealSubject

1
2
3
4
5
6
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject request");
}
}

实现InvocationHandler,此处做具体的处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SubjectHandlerImpl implements InvocationHandler {
private Object realSubject;
public SubjectHandlerImpl(Object subject) {
this.realSubject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnObject = null;
//检查realSubject是否和proxy实现了相同的接口
if(Subject.class.isInstance(realSubject)) {
System.out.println("SubjectHandlerImpl invoke");
returnObject = method.invoke(realSubject, args);
}
return returnObject;
}
}

客户使用:

1
2
3
4
5
6
7
8
public static void main(String args[]) {
RealSubject realSubject = new RealSubject();
SubjectHandlerImpl subjectHandlerImpl = new SubjectHandlerImpl(realSubject);
Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[] {Subject.class}, subjectHandlerImpl);
proxy.request();
}

Log信息:

SubjectHandlerImpl invoke
RealSubject request

通过例子和UMl图来看,Proxy是Jdk提供的类,调用

1
Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class[] {Subject.class},subjectHandlerImpl);

动态生成的了一个Subject对象proxy,其中所有对proxy的调用都会被传入SubjectHandlerImpl的invoke中,看下invoke中参数的意义。

1
public Object invoke(Object proxy, Method method, Object[] args)
  • proxy 就是上面通过Proxy.newProxyInstance生成的对象proxy

  • method 是proxy的调用的任何方法

  • args 是method的参数

那proxy对应的类是什么?又是怎么将方法的调用转换到InvocationHandler的invoke的?看下

1
2
3
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

传入的三个参数分别为需要代理类的ClassLoader, interfaces是代理类和RealSubject需要共同实现的所有接口,这些接口也是由newProxyInstance最后生成的对象所实现的接口,看到有些博客中将这个interfaces传入的是realSubject.getClass().getInterfaces(),我觉得不是很妥当,因为realSubject可能实现了一些其他的接口,而这些接口和Subject无关,h就是需要自己实现了InvocationHandler的对象。

以上面的例子来看最后生成的代理类是什么样的:

//为方便理解,省去equals,toString,hashCode等方法的代码,实现和request相同。同时做一些小的调整,不改变原有逻辑
public final class ProxySubject extends Proxy implements Subject
{
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;
    InvocationHandler h;


    public ProxySubject(InvocationHandler paramInvocationHandler)
    {
        h = paramInvocationHandler;
    }

    @Override
    public void request() {

        this.h.invoke(this, m3, null);
    }

    static
    {
       m3 = Class.forName("Subject").getMethod("request");
    }      
}

这个类的生成最终是在Proxy中是在通过native方法Proxy.generateProxy生成类的字节码,再经过ClassLoader加载,就像class文件就被加载的过程,然后用newInstance方式生成最终的Proxy对象,具体的细节可以自行查看源码或Google,不是本篇博客的重点。

现在来看ProxySubject这个类,实现了Subject类,也就是newProxyInstance传入的第二个参数的类,所以会实现request()方法,当调用request()方法时,这个h也就是newProxyInstance第三个参数自行实现的的InvocationHandler,这时就调用到了InvocationHandler.invoke(Object proxy, Method method, Object[] args),这个proxy也就是最后生成的Proxy对象,method就是m3,m3对应的就是Subject的request。

动态代理的应用

通过上面的分析,对动态代理应该有了一些认识,但应该还是有些迷惑,说了这么多,到底实际中该怎么用,有哪些使用场景,可以通过一种Android中的事件总线库——Router来更详细的了解。