代理模式是二十多种设计模式中的一个,属于比较常用的设计模式。本质上就是用来委托我们生成的代理类去完成一些额外的功能,这样能够达到解耦、封装的目的。
通常可以用在RPC、AOP中。比如在RPC中,当我们调用远程方法时,需要委托代理类帮助我们去通过网络连接远程的服务提供者,帮助我们将消息编码发送给服务端,帮我们接受服务端发来的结果。

静态代理

在代理模式中,通常指的时静态代理。
静态代理

这是静态代理的UML图。代理的思想是:代理类ProxySubject拥有实际类RealSubject的相同方法doSomething(实现相同的一个接口),同时代理类内聚了实际类(即传入实际类的一个引用),在代理类的doSomething方法中,通过实际类的引用调用实际类的doSomething方法,并在调用前后加入需要代理实现的额外功能。这样我们就可以通过调用代理类的相同方法来达到我们的目的。
举个例子:

    interface Subject{
        void doSomething();
    }
class RealSubject implements Subject{
    void doSomething(){
        System.out.println("doSomething");
    }
}
class ProxySubject implements Subject{
    private Subject target;
    public ProxySubject(Subject target){
        this.target = target;
    }
    void doSomething(){
        //do before
        System.out.println("do before");
        
        target.doSomething();//调用realSubject的doSomething方法
        
        //do after
        System.out.println("do after");
    }
}
class ProxyTest{
    public static void main(String args[]){
        //生成代理类,并将实际类传入
        ProxySubject proxy = new ProxySubject(new RealSubject());
        proxy.doSomething();
    }
}

以上,通过代理类执行实际类相同的方法,我们可以运行额外的功能。这里不仅会运行实际类doSomething方法里面的打印“doSomething”,还会在之前打印“do before",之后打印”do after",这两个即为额外的功能。

动态代理

上面的静态代理其实存在一个问题,一个实际类对应一个代理类,而很多时候需要代理实现的额外功能是相同的,比如我们要在A类的某个方法调用前打印“before”,B类的某个方法调用前打印“before”,C类也有这个需求,如果用静态代理,我们需要写A类的代理,B类的代理,C类的代理。。。这样岂不是要累死。
java当中的动态代理就是为了这个解决这个问题。其实思路也很简单,不用去管A类、B类、C类还是什么其他类,统一用反射调用实际类的方法。
java提供了动态代理类Proxy,使用其中的静态方法Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)来动态生成代理类。interfaces为实际类的接口,对应上面图中的Subject,loader为接口的Classloader,InvocationHandler接口有一个invoke方法需要自己实现,我们委托代理类实现的额外功能便放在该方法里。需要注意的是由此看出java的动态代理需要有接口才能使用。如果不用接口实现动态代理只能求助于cglib这类字节码增强框架。
下面是动态代理的一般实现方法:

public class InvokeProxy implements InvocationHandler{
    private Object target;
    
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        
        System.out.println("do something before");
        method.invoke(target, args);
        System.out.println("do something after");
        
        return null;

        /*
        如果反射调用的方法有返回值:
        Object result;
        result = method.invoke(target, args);
        return result;
        */
    }

}

同样有接口Subject:

 interface Subject{
        void doSomething();
    }

假设类A实现了Subject接口,可以根据如下代码生成A的代理

public class ProxyTest {

    public static void main(String[] args) {
        Subject proxyA = (Subject)new InvokeProxy().bind(new A());
        proxyA.doSomething();
    }

}

如果还有类B实现了Subject接口,我们只需更改上面的代码即可,而不用像静态代理那样重复的编写代码。这就是动态代理相对于静态代理的意义。


ethfoo
46 声望5 粉丝