Java设计模式-代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。


代理模式顾名思义,有个代理,那这个代理做什么工作呢?我们平时生活中,我们遇到过的代理就是各个品牌的代理,一个产品不能让广大的消费者去工厂去买吧,工厂生产出产品,各个地方有一个代理,代理这个工厂负责个每个商店推销产品。又比如一个歌手有一个助理,很多事情都是助理负责,助理接收到信息,看看是什么信息,自己可以干的就自己完成,就比如歌手的助理收到要去开演唱会的通知,助理不能唱歌啊,这个核心的事情还是得交给老板,助理的作用就是避免自己的老板受到打扰。所以代理是过滤掉一些事情,剩下的核心内容还是由真实的对象来负责。


代理模式存在的意义就是,降低了真实对象和要做事情的耦合性,降低了他们之间的联系,代理类对请求进行过滤,预处理,将请求交给真实对象,或者委派给真实对象。好比消费者不会去工厂去买产品,一个歌手不会每天都会接收到很多没有用的信息。


同样代理模式也符合开闭原则,对修改关闭,对扩展开放。在不改变真实类的情况下,对其类进行扩展。代理类对外提供一系列的接口,用户对其使用不会对原对象产生影响,对原对象有一个良好的封装性。


代理模式分为静态代理和动态代理,还有一种是Cglib代理,这里就不介绍了。

静态代理:

抽象角色:是一个接口,定义了行为,没有具体实现

package demo_staticagent;

public interface AbstractSubject {

    public abstract void sing();
}

代理角色:其中有真实对象的引用,从而对真实对象进行操作,可以对真实对象的操作进行扩展,不会修改原对象。

package demo_staticagent;

public class ProxySubject implements AbstractSubject {
    
    private AbstractSubject abstractSubject;

    public ProxySubject(AbstractSubject abstractSubject) {
        this.abstractSubject = abstractSubject;
    }


    @Override
    public void sing() {
        abstractSubject.sing();
    }
}

真实对象:有对事件的真实操作,最终我们要引用的对象。
package demo_staticagent;

public class RealSubject implements AbstractSubject{

    @Override
    public void sing() {
        System.out.println("真唱");
    }
}

测试类:
package demo_staticagent;

public class Test {

    public static void main(String[] args) {
        RealSubject reaal = new RealSubject();
        ProxySubject proxy = new ProxySubject(reaal);
        proxy.sing();

    }
}

输出:
真唱

从输出结果可以看出,是真唱不是假唱。我们创建了真实对象和代理对象,用代理模式进行调用,通过代理最终我们核心的代码,执行的还是真实角色中的代码。知识对真实角色进行了封装。


动态代理:

动态代理相比于静态代理,动态代理的代理类是在动态生成的,也就是jvm通过反射获取代码生成代理类,所以用户并不能决定代理角色和真实角色之间的联系,而是由程序运行时候决定的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。


动态代理实现的三步走:

  • 实现InvocationHandler接口,创建自己的调用处理器 。
  • 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
  • 执行真实角色具体任务。

要想创建处理器必须实现InvocationHandler接口,之后通过类装载器创建代理类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。


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

proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数


我们依旧要创建抽象角色和真实角色类

package demo_proxy;

public interface AbstractSubject {

    public abstract void sing();
}
package demo_proxy;

public class RealSubject implements AbstractSubject{

    @Override
    public void sing() {
        System.out.println("真唱");
    }
}

代理角色:和静态代理相比,静态代理运用对象调用完成代理,而动态代理通过实现InvocationHandler接口,创建了一个调用处理器,其实是通过Java的反射机制完成

package demo_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxySubject implements InvocationHandler {

    public AbstractSubject abstractSubject;

    public ProxySubject(AbstractSubject abstractSubject) {
        this.abstractSubject = abstractSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(abstractSubject, args);
        return null;
    }

}
当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

测试类:

package demo_proxy;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        /*代理的真实对象*/
        RealSubject real = new RealSubject();
        /*代理哪个对象就把真实对象传进去*/
        ProxySubject proxySubject = new ProxySubject(real);
         /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        AbstractSubject a = (AbstractSubject) Proxy.newProxyInstance(proxySubject.getClass().getClassLoader, 
                real.getClass().getInterfaced(), proxySubject);
        a.sing();
    }
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口
              (多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

看上去实现很简单,我们只需要实现接口,调用其中的方法就可以实现动态代理。

通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法。


输出:

真唱


参考:

http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

http://www.jb51.net/article/86531.htm


原文地址:https://www.cnblogs.com/duzhentong/p/8576527.html