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