java设计模式之代理模式

来源:互联网 发布:left软件安卓版 编辑:程序博客网 时间:2024/05/21 22:50

1.  简介

代理模式的定义:Provide a surrogate or placeholder for another object to controlaccess to it(为其他对象提供一种代理以控制对这个对象的访问)。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能

2.  UML类图


3.  模式中包含的角色及其职责

Sourceable:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。

source:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。

Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志)

4.  代码实现

package com.deppon.tps.module.TestProxyPattern;public interface Sourceable {public void method(); }
package com.deppon.tps.module.TestProxyPattern;public class Source implements Sourceable{@Overridepublic void method() {System.out.println("原方法。。");}}
package com.deppon.tps.module.TestProxyPattern;public class Proxy implements Sourceable{private Source source;public Proxy(){this.source=new Source();}@Overridepublic void method() {doBefor();source.method();doAfter();}public void doBefor(){System.out.println("原始方法之前");}public void doAfter(){System.out.println("原始方法之后");}}
package com.deppon.tps.module.TestProxyPattern;public class Test {public static void main(String[] args) {Proxy proxy=new Proxy();proxy.method();}}
测试结果如下:



在装饰器模式和代理模式之间还是有很多差别的:

装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。详见:http://blog.csdn.net/hitprince/article/details/6794748

5.   应用场景

现实世界中,秘书就相当于一个代理,老板开会,那么通知员工开会时间、布置会场、会后整理会场等等开会相关工作就可以交给秘书做,老板就只需要开会就行了,不需要亲自做那些事。同理,在我们程序设计中也可使用代理模式来将由一系列无关逻辑组合在一起的代码进行解耦合,比如业务代码中的日志代码就可以在代理中进行。spring的AOP就是典型的动态代理应用。

6.   代理模式的应用形式

(1)远程代理(Remote Proxy) -可以隐藏一个对象存在于不同地址空间的事实。也使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。

(2)虚拟代理(Virtual Proxy) – 允许内存开销较大的对象在需要的时候创建。只有我们真正需要这个对象的时候才创建。

(3)写入时复制代理(Copy-On-Write Proxy) – 用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。是虚拟代理的一个变体。

(4)保护代理(Protection (Access)Proxy) – 为不同的客户提供不同级别的目标对象访问权限

(5)缓存代理(Cache Proxy) – 为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。

(6)防火墙代理(Firewall Proxy) – 控制网络资源的访问,保护主题免于恶意客户的侵害。

(7)同步代理(SynchronizationProxy) – 在多线程的情况下为主题提供安全的访问。

(8)智能引用代理(Smart ReferenceProxy) - 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

(9)复杂隐藏代理(Complexity HidingProxy) – 用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Façade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。

7、静态代理的实例

下面以一个延迟加载的例子来说明一下静态代理。我们在启动某个服务系统时, 加载某一个类时可能会耗费很长时间。为了获取更好的性能, 在启动系统的时候, 我们往往不去初始化这个复杂的类, 取而代之的是去初始化其代理类。这样将耗费资源多的方法使用代理进行分离, 可以加快系统的启动速度, 减少用户等待的时间。

定义一个主题接口

public interface Subject {  public void sayHello();  public void sayGoodBye();}

?

定义一个目标类, 并实现主题接口

public class RealSubject implements Subject {  public void sayHello() {    System.out.println("Hello World");  }  public void sayGoodBye() {    System.out.println("GoodBye World");  }}

?

定义一个代理类, 来代理目标对象。

public class StaticProxy implements Subject {  Private RealSubject realSubject = null;  public StaticProxy() {}  public void sayHello() {    //用到时候才加载, 懒加载    if(realSubject == null) {      realSubject = new RealSubject();    }    realSubject.sayHello();  }  //sayGoodbye方法同理  ...}

?

定义一个客户端

public class Client {  public static void main(String [] args) {    StaticProxy sp = new StaticProxy();    sp.sayHello();    sp.sayGoodBye();  }}

?

以上就是静态代理的一个简单测试例子。感觉可能没有实际用途。然而并非如此。使用代理我们还可以将目标对象的方法进行改造, 比如数据库连接池中创建了一系列连接, 为了保证不频繁的打开连接,这些连接是几乎不会关闭的。然而我们编程总有习惯去将打开的Connection去close。 这样我们就可以利用代理模式来重新代理Connection接口中的close方法, 改变为回收到数据库连接池中而不是真正的执行Connection#close方法。其他的例子还有很多, 具体需要自己体会。详见:http://skyuck.iteye.com/blog/524561


8、动态代理

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。

不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;
使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
生成动态代理的方法有很多: JDK中自带动态代理, CGlib, javassist等。这些方法各有优缺点

使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。



 

使用动态代理的五大步骤
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
 
2.通过Proxy.getProxyClass获得动态代理类
 
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
 
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
 
5.通过代理对象调用目标方法
 
例1(方式一)

package com.deppon.tps.module.TestProxyPattern.example1;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class MyProxy {public interface IHello{        void sayHello();    }    static class Hello implements IHello{        public void sayHello() {            System.out.println("Hello world!!");        }    }    //自定义InvocationHandler    static  class HWInvocationHandler implements InvocationHandler{        //目标对象        private Object target;        public HWInvocationHandler(Object target){            this.target = target;        }        //proxy:com.deppon.tps.module.TestProxyPattern.example1.MyProxy$Hello@134a7d8        //method:public abstract void com.deppon.tps.module.TestProxyPattern.example1.MyProxy$IHello.sayHello()        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            System.out.println("------插入前置通知代码-------------");            //执行相应的目标方法            Object rs = method.invoke(target,args);            System.out.println("------插入后置处理代码-------------");            return rs;        }    }    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,  InstantiationException, IllegalArgumentException, InvocationTargetException {      //生成$Proxy0的class文件        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");        //获取动态代理类($Proxy0)        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);        //获得代理类的构造函数,并传入参数类型InvocationHandler.class        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);        //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入        IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));        //通过代理对象调用目标方法invoke()        iHello.sayHello();    }}
例2(方式二)
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {       //生成$Proxy0的class文件       System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");       IHello  ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加载接口的类加载器               new Class[]{IHello.class},      //一组接口               new HWInvocationHandler(new Hello())); //自定义的InvocationHandler       ihello.sayHello();   }

运行结果:



 
1 0
原创粉丝点击