代理模式是Java设计模式中非常常用的一种,其具体分为静态代理和动态代理两种。

静态代理是被代理对象和代理之间是一对一的关系,一个代理只可以代理一个被代理对象,在编程阶段其关系就已经确定,不能修改。所以,静态代理由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理是被代理对象和代理之间是一对多的关系,可以代理一类型的被代理对象,编程阶段通过JDK自带的InvocationHandler实现类似模板的方法,具体执行的函数根据传入的参数决定。所以,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。

通过代码来更详细解释

Java Static Proxy

首先需要一个公共的接口,目的为了调用和被代理对象相同的方法。同时proxy会持有一个被代理对象的实例,比如StarProxy的私有成员变量就是接口类型Actor[在测试类中传入的是实现了Actor接口的真实的被代理类Star]。这样在StarProxy的Perform方法中,可以调用Star的perform方法,再加入StarProxy的自己一些操作。所以当在测试类中,首先初始化Star类,然后作为参数传入StarProxy,当调用StarProxy的perform方法时,也会调用Star的perform。这就完成了,歌星和经纪人的一个日常活动。我们想找歌星表演,一般都是直接找经纪人,一旦达成协议,他会联系歌星进行表演活动。

//common interface for proxy
public interface Actor {
    void perform();
}

//real subject which will be delegated
public class Star implements Actor {
    @Override
    public void perform() {
        System.out.println("Star perform");
    }
}

//proxy subject which will delegate real subject
public class StarProxy implements Actor {
    private Actor actor;

    public StarProxy(Actor actor) {
        this.actor = actor;
    }

    @Override
    public void perform() {
        System.out.println("StarProxy starts a case");
        System.out.println("StarProxy discuss the money");
        actor.perform();
        System.out.println("StarProxy receive the money");
    }
}

//test proxy class
public class Test {
    public static void main(String[] args) {
        Star star = new Star();
        StarProxy starProxy = new StarProxy(star);
        starProxy.perform();
    }
}

静态模式的特点是,首先代理动作是通过持有被代理对象的实例完成的,所以如果需要代理多个对象,就要显示的声明多个成员变量。如果遇到一个经纪人需要代理多个歌星的时候,业务逻辑都是相同的,就要为每个歌星写对应的方法。比如有A,B,C三个歌星,是相同的经纪人Proxy,这种情况下Proxy要持有A,B,C三个类的实例并且分别实现performA,performB,performC三个方法才能达到分别代理的目的。所以静态代理多数情况下,代码重用率比较低,内存消耗也更多。

Java Dynamic Proxy

动态代理是用于一个代理类可以代理一类被代理对象。实现方式是通过实现JDK自带的InvocationHandler接口来完成的。首先需要一个接口Singer,真正的被代理类实现这个接口。然后,代理类SingerProxy这里并不需要实现接口Singer而是实现JDK自带接口InvocationHandler,并且重写invoke方法。invoke方法就是实现动态代理的关键,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。所以如果是相同逻辑只是被代理的类不同,可以通用的写在invoke方法中。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

//proxy: 指代我们所代理的那个真实对象
//method: 指代的是我们所要调用真实对象的某个方法的Method对象
//args: 指代的是调用真实对象某个方法时接受的参数
public interface Singer {
    void sing();
    void concert();
}

public class SingStar implements Singer {
    @Override
    public void concert() {
        System.out.println("Make a concert");
    }

    @Override
    public void sing() {
        System.out.println("Sing a song");
    }
}

public class SingerProxy implements InvocationHandler {
    private Object obj;

    public SingerProxy(Object newObj) {
        this.obj = newObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("recevice a case");
        if(method.getName() == "sing") {
            System.out.println("discuss the sing performance fee");
        }
        else if(method.getName() == "concert") {
            System.out.println("discuss the concert fee");
        }
        Object result = method.invoke(obj,args);
        return result;
    }
}

public class TestSingerProxy {
    public static void main(String[] args){
        SingStar star = new SingStar();
        InvocationHandler handler = new SingerProxy(star);

        Singer proxy = (Singer) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                star.getClass().getInterfaces(),handler);

        proxy.concert();
        proxy.sing();
    }
}

在TestSingerProxy中,最关键的就是调用Proxy的静态方法newProxyInstance。Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是newProxyInstance这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

//loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载,一般就是代理类的ClassLoader 
//interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
//h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

之后,作为一个Singer接口类型的proxy,调用任何接口定义的方法,都会自动调用invoke和invoke其中写好的对应真正被代理类型的方法。这样只要是一类被代理对象,就可以使用一个代理类完成代理模式。