再看代理模式

参考:
Java 技术之动态代理机制
代理模式及Java实现动态代理

静态代理

需求:假如对于接口A来说,其实现类ImplementA类实现了A接口的方法,现在若要在ImplementA实现的方法的基础上再加入新的操作,如计时,检查等,我们可以使用继承(随之会带来继承泛滥,因此继承常用于有较强层级关系的类中),也可以参考装饰者模式中的思想,即实现一个新的实现类NewA,该类通过调用ImplementA类对应方法,并在其前后加入新操作来满足要求。这种情况也叫做静态代理,NewA就是我们的静态代理类。

关于静态代理和装饰者模式的区别可以参考:装饰者模式,代理模式与适配器模式的比较,代理模式和装饰者模式。
我的理解是,装饰者模式更倾向于不同装饰类之间的嵌套组合,类似搭积木,从而达到“个性化定制”,最有特点的就是Java的I/O类。而静态代理则更倾向于控制委托类,即调用者不会关心具体的委托类,也不会嵌套代理类。静态代理类相当于承担一个把关的功能,而不是自由组合。

动态代理

假如一个实现类ImplementA实现了A,B,C,D…很多接口的方法,那此时再用静态代理类去完成添加新功能就显得十分繁琐。为此,我们提出了动态代理的概念,即如果对A,B,C,D…的方法要添加同样的一种操作,则可以将该操作统一写成如下形式:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //新添预处理操作
        Object result = method.invoke(implementA, args);        //Method类的实例对象,用到了反射的思想
        //新添后处理操作
        return result;
    }

如上,我们统一把新增操作写一遍,中间利用反射的方法去让程序自己根据情况进行调用对应的ImplementA的方法。
我们规定了一个接口用于实现invoke方法,即InvocationHandler(调用处理器)接口。该接口主要功能就是定义新增操作的。接着,由于用到了反射的思想,要想动态识别是哪个接口的具体方法,我们还需要在运行过程中“动态”地新建一个动态代理类(该类和ImplementA实现的接口一样),然后将这个动态代理类绑定刚才的InvocationHandler对象并实例化,就可以调用对应ImplementA的修改过的方法了。

静态代理和动态代理的区别

静态代理类是我们已经写好的类,因此在编译时,JVM会直接把现成的.class文件加载进去。
动态代理类预先写好的只有附加操作,但是方法的调用是需要具体类的具体实例的,因此,我们需要利用反射机制,在程序运行的过程中,自动的去根据已有的委托类去编写对应的“静态代理类”的.class文件,然后调用对应的类加载器,把这个程序自动写好的.class文件重新加载到JVM之中(加载到永久代/元空间里面)。接着,我们才可以用这个热乎的刚加进来的.class文件去实例化,产生相应的对象在Java的堆中。然后我们才能正常通过实例对象调用具体方法。
为什么要如此复杂呢?因为这样就可以将包含新增操作的类(InvocationHandler),用于连接委托类和包含新增操作类的动态代理类(Proxy)和委托类(eg. ImplementA)相互间的关系给解耦和,达到复用的操作。
还要注意的是,动态代理类Proxy的静态方法返回的是一个继承于Proxy类的子类,通常,我们返回的是Object类型,然后根据要调用的具体方法属于哪个接口再将其强制转换成对应的接口类,以实现正常调用。

你可能感兴趣的:(设计模式)