静态代理

代理模式的意思是,当一个类的对象要去做某个行为时,不是由他自己做,而是委派给一个专门的代理者去完成。例如老板要见一个人,可以让秘书代老板去接待,而不用老板亲自去。这样做的好处是。这样做的好处是代理者可以在完成被代理者的任务之前/之后执行一些别的操作,例如写日志等。由代理类去做这些事可以让核心类的逻辑不受影响,相当于起到了一个委托的作用。

静态代理的意思是被代理类已经在编译时就已经写好了,下面看一个例子:
抽象出一个接待客人的接口:

public interface ReceiveBehaviour{
    void receive();
}

老板类实现这个接口:

public class Boss implements ReceiveBehaviour {
    @override
    public void receive(){
        System.out.println("接待客人");
    }
}

创建一个秘书类(即老板的代理类)也是实现接待客人的接口,同时在使用创建代理类对象的时候要获得被代理的对象:

public class BossProxy implements ReceiveBehaviour {
    private ReceiveBehaviour boss;
    public BossProxy(){
        this.boss = new Boss();
    }
    @Override
    public void receive() {
        System.out.println("我代为接待你,我给你斟茶递水,我记下现在的时间");
        boss.receive();
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        BossProxy secretary = new BossProxy();
        secretary.receive();
    }
}

输出:

我代为接待你,我给你斟茶递水,我记下现在的时间
接待客人

以上就完成了静态代理的过程。
总结:当要执行某个类的行为时,不是直接调用该类的方法,而是把该类的对象转交给一个代理类,让它的代理类去实现。这样能够在代理类执行被代理者的方法前/后中增加一些别的行为和操作。示意图如下:
图片说明
缺点:如果要代理的对象有很多方法,被代理者要一个一个方法实现。如果需要代理的类还不止一个,那要实现的方法更多了。所以需要动态代理。

动态代理

动态代理就是负责代理的对象是在运行时才产生的,并非在编译时就写死的。要实现这个功能,首先我们依然定义一个接口,抽象出某些行为:

public interface ReceiveBehaviour{
    void receive();
}

依然有一个原始类来实现这个接口:

public class Boss implements ReceiveBehaviour {
    @override
    public void receive(){
        System.out.println("接待客人");
    }
}

下面写代理类,要实现动态代理,代理类是指定义了代理行为的类,要实现invocationHandler接口,具体如代码:

public class BossProxy implements invocationHandler{
    private Object subject;  //要代理的真是对象
    public BossProxy(Object subject){  //构造函数,传入要被代理的对象
    this.subject = subject;
}


@override
public Object invoke(Object object, Method method, Object[] args){
    System.out.println(“我是代理者,这是代理者的行为”);
    method.invoke(subject, args);
    }
}

实现了invocationHandler最重要的用处就是现在此类的对象可以在接下来与被代理对象进行绑定了,详见:

public Test {
public static void main(String args[]){
    //创建要被代理的对象
    Boss boss = new Boss();
    //创建代理类的对象,将要代理的对象传进去
    BossProxy secretary = new BossProxy(bo***Proxy类的newProxyInstance静态方法绑定被代理对象和代理对象(具体解释见下文),返回一个可以代理对象
    ReceiveBehaviour receiveBehaviour = (ReceiveBehaviour)Proxy.newProxyInstance(boss.getClass().getClassLoader(), boss
                .getClass().getInterfaces(), secretary);
    //然后就可以使用这个代理对象执行被代理者的各种方法了,同时也会被加上代理类的行为,原因是因为在调用被代理者的方法时,实际上时通过了代理者重写的invoke方法执行的,所以增加了代理者的行为
    receiveBehaviour.receive();
}
}

输出:

我是代理者,这是代理者的行为
接待客人

下面对Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException进行解释:

  • loader:要求输入被代理对象的类加载器、
  • interfaces:被代理对象实现的接口(或接口数组)、
  • h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
  • 这个方法返回的是一个JVM运行时动态生成的对象。正是因为有这个方法,我们才可以在返回的代理对象上执行被代理者的方法(或者说是执行被代理者中 实现了 且传入了interfaces中的接口 的方法)。

下图为我总结的动态代理的使用步骤:

  1. 创建接口及实现类
  2. 实现代理处理器:实现 InvokationHandler ,实现 invoke()方法
  3. 通过 Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 获得动态代理对象。
  4. 通过代理对象调用方法。

示意图如下所示:
图片说明

对比

通过上面的动态代理和静态代理的对比我们能总结出两者的不同:

  1. 负责定义代理者行为的,即实现了InvocationHandler接口的类,不需要实现被代理者类所实现的行为。这是合理的,例如一个代理者是负责写日志的,它只需要关心把日志写好,不用关心它代理那方的具体行为是什么。正是因为重写InvocationHandler中的invoke方法,任何被代理的类执行方法,最后都会进入代理者的invoke方法处执行,所以并不需要在代理者中重写被代理者的方法。
  2. 代理者不用重写被代理者的方法,但是却在invoke类中根据执行方法的不同进行不同的操作。因为留意到invoke方法中是传入了method对象的,这个对象就是被代理者即将要执行的方法,因此此时可以根据传入的method对象的不同进行不同的处理。
  3. 定义了代理者行为的类,可以代理各种不同的类。正如例子中所呈现的,实际上除了Boss类可以传入BossProxy类,任何别的类也可以传进去,然后交由BossProxy进行代理。
  4. 在最后绑定实现了InvocationHandler类和需要被代理的类时我们发现,传入的接口是弹性的。可能被代理者实现了很多接口A、B、C、D,但是现在只想在执行A、B接口中的方法时被使用代理,就可以只传入[A, B]。