代理模式

来源:互联网 发布:pspice仿真软件下载 编辑:程序博客网 时间:2024/06/07 13:06

代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

代理模式结构图如下:
这里写图片描述

代理模式结构图代码解析:

//代理类和真实类的公共接口(接口或抽象类)public abstract class Subject{      public abstract void request();}
//真实类,RealSubject类,定义 Proxy类所代表的真实实体public class RealSubject extends Subject{    @Override    public void reqeust(){        print("真实的请求....");    }}
//代理类,Proxy类,用来保存一个引用使得代理可以访问实体,并提供一个与Subject接口相同的接口,这样代理类就可以替代实体。public class Proxy extends Subject{    RealSubject realSubject;  //Spring的DI在这里挺好用    //调用Proxy的request方法就等于调用真实类的reqeust方法    @Override    public void reqeust(){        if(realSubject == null){            realSubject = new RealSubject();        }        realSubject.Reqeust();    }}
//客户端代码public class Main{    public static void main(String[] args){        //创建代理类        Proxy proxy = new Proxy();        proxy.reqeust();  //内部实质调用了真实类的request()方法    }}

代理模式的应用:
    ①远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
   ②. 虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的对象。这样可以达到性能的优化,比如在打开一个很大的HTML网页,里面有很多文字和图片,但还是可以很快的打开它,此时会先看到文字,而图片是一张一张下载后才能看到。那些没有打开的图片框,就是通过虚拟代理替代了真实的图片,此时代理存储了真实图片的路径和尺寸。
   ③. 安全代理:用来控制对真实对象访问时的权限。一般用于对象应该有不同访问权限的时候。
   ④. 智能代理:指当调用真实对象时,代理处理另外一些事。

      上述代理模式是最简单的一种代理,即静态代理。静态代理的优点是:可以在不修改原类的情况下,对原来的类进行扩展。但它的缺点也很明显:因为代理类要和目标对象类实现相同的接口,因此会有很多代理类,并且如果接口中增加方法,代理类和目标对象类都要进行维护。

解决静态代理缺点的方式就是使用动态代理。
动态代理的优点有哪些?
   ①. 代理类不用实现和目标类一样的接口。
   ②. 代理对象的产生是利用JDK的API,动态的在内存中构建代理对象<需要我们指定创建代理对象/目标对象实现的接口的类型>。
    ③. 动态代理,也称jdk代理,接口代理。

JDK中生成代理对象的API:
    代理类所在的包:java.lang.reflect.Proxy
    JDK实现代理需要使用一个方法:newProxyInstance方法,该方法有三个参数。方法的完整写法时:static Object newProxyInstance(ClassLoader loader,Class

/** * Created by 杨Sir on 2017/11/14. * 动态代理的接口 */public interface IGiveGift {    //送花的方法    void GiveFlowers();}
/** * Created by 杨Sir on 2017/11/14. */public class Girl {    private String name;    public Girl() {    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}
/** * Created by 杨Sir on 2017/11/14. * 目标类 */public class Pursuit implements IGiveGift {    Girl girl;    public Pursuit(Girl girl) {        this.girl = girl;    }    @Override    public void GiveFlowers() {        System.out.println(girl.getName()+ ": 送你鲜花。");    }}
 /** * Created by 杨Sir on 2017/11/14. * 动态代理类,创建代理对象, * 不要实现接口,但要指定和目标对象一样的接口类型 */public class DynamicPorxy {    //维护一个目标对象    private Object target;    public DynamicPorxy(Object target) {        this.target = target;    }    //给目标对象生成代理对象    public Object getProxyInstance(){        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                System.out.println("去买鲜花");                //执行目标对象的方法                Object returnValue = method.invoke(target,args);                System.out.println("送花完成");                return returnValue;            }        });    }}
public class Main {    public static void main(String[] args) {        //创建一个Girl对象        Girl girl = new Girl();        girl.setName("校花");        //目标对象        Pursuit pursuit = new Pursuit(girl);        //根据目标对象生成代理对象,这个代理对象在不改动目标类的情况下扩展了目标类的功能。        IGiveGift giveGift = (IGiveGift) new DynamicPorxy(pursuit).getProxyInstance();        //调用代理对象的方法        giveGift.GiveFlowers();    }}

需要注意的是,使用动态代理时,代理类虽然不需要实现接口,但是目标对象类一定要实现接口,否则不能用动态代理。

      上面的两种代理方式都需要目标类实现一个接口,当如果目标类没有实现接口的话,那怎么创建目标对象的代理对象呢?答案是使用 Cglib 代理。
      前面说的静态代理和动态代理都需要目标对象实现一个接口,当如果目标类没有实现任何接口的话,那怎么创建目标对象的代理对象呢?答案是使用 Cglib代理。

Cglib代理,也叫做子类代理,它通过在内存中构建一个子类对象从而实现对目标对象的扩展。
       JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就需要使用Cglib代理实现。
       Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,进而为他们提供方法的interception(拦截)。
       Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib代理的实现方法:
      引入Cglib的jar包, 幸运的是Spring的core包已经引入了CGLIB的jar包,所以使用Spring的项目不用再次引入cglib的jar。
      引入jar后,即可在内存中构建代理目标类的子类。
       代理的类不能加 final。
      目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象之外的业务方法。
      在Spring的面向切面编程(aop)中,如果加入容器的目标对象实现了接口,用动态代理,否则用 CGLIB代理。

举个简单例子看一下:

/** * Created by 杨Sir on 2017/11/15. * 目标类 */public class Pursuit {    public void GiveFlowers() {        System.out.println("我是目标类。");    }}
/** * Created by 杨Sir on 2017/11/15. * CGLIB代理 创建目标对象的代理对象 */public class CglibProxy implements MethodInterceptor {    //维护目标对象    private Object target;    public CglibProxy(Object target) {        this.target = target;    }    //为目标对象创建代理对象    public Object getProxyInstance(){        //工具类        Enhancer enhancer = new Enhancer();        //设置父类        enhancer.setSuperclass(target.getClass());        //设置回调函数        enhancer.setCallback(this);        //创建代理对象(目标类的子类)        return enhancer.create();    }    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        System.out.println("购买鲜花...");        //执行目标对象的方法        Object returnVal = method.invoke(target,objects);  //传入目标对象和方法的参数        System.out.println("送花完成...很开心");        return returnVal;    }}
/** * Created by 杨Sir on 2017/11/15. * 测试 */public class Main {    public static void main(String[] args) {        //创建目标对象        Pursuit pursuit = new Pursuit();        //代理对象        Pursuit proxy = (Pursuit) new CglibProxy(pursuit).getProxyInstance();        //执行代理对象方法,送花        proxy.GiveFlowers();    }}
原创粉丝点击