首页 > 代码库 > 代理模式

代理模式

代理模式的定义 :

为其他对象提供一种代理以控制对这个对象的访问 . 代理对象起到的是中介的作用 , 可去掉功能服务或添加额外的服务 .

常见的代理模式简单分为以下几种 :

  • 远程代理
  • 虚拟代理
  • 保护代理
  • 智能引用代理

远程代理 : 类似于客户端和服务端的关系 , 为不同地理位置的对象提供局域网代表对象 .

虚拟代理 : 根据需要将资源消耗很大的对象进行延迟 , 真正需要的时候再进行创建 .

      比如 : 当一个网页在加载一个图片时 , 需要比较长的时间 , 这个时候需要等待比较长的时间 , 会影响其他的操作 , 这时我们可以在加载之前用一张虚拟的图片进行填充其位置 , 等到真实的图片加载完毕 , 再进行替换 .

保护代理 : 可以用来控制权限 .

      比如 : 在一个 BBS 系统中 , 不同类别的用户有不同的权限 , 未登录用户只能浏览帖子 , 登录了的用户能发帖和评论 , 以及删除自己的帖子或者评论 , 但是不能删除别人的贴子 , 同时 , 管理员的权限是可以删除任意帖子 .

智能引用代理 : 提供对目标对象一些额外的服务 , 同时也会减少一些服务 .

      比如 : 火车票代售点 , 可以代理火车站售票 , 支持电话预定 ( 这个功能火车站没有 ) , 但是不支持退票 .

 

实现代理的方式有两种 :

  • 静态代理
  • 动态代理

静态代理 : 代理和被代理对象在代理之前是确定的 , 并且他们都实现了相同的接口或者继承了相同的抽象类 .

技术分享

静态代理有种方法 :

  • 继承
  • 聚合

 动态代理 : 代理类是不确定的 .

 

动态代理也有两种方法 :

  • JDK动态代理
  • CGLIB动态代理

 JDK动态代理实现的步骤 :

  1. 创建一个实现接口 InvocationHandler的类 , 它必须实现 invoke 方法
  2. 创建被代理的类以及接口
  3. 调用 Proxy 类的静态方法 , 创建一个代理类
  4. 通过代理调用方法

举个栗子 :

       一个汽车类 , 实现了Moveable接口 , 有 move的方法 , 现在需要使用代理实现一个计算运行时间的功能 , 也就是给move方法进行功能的增强 .

  两个基本类 :

      Moveable 接口

package com.msym.jdkproxy;public interface Moveable {    void move();}

     Car 类

package com.msym.jdkproxy;import java.util.Random;public class Car implements Moveable{    @Override    public void move() {        System.out.println("车子开起来....");        try {            Thread.sleep(new Random().nextInt(1000));        } catch (InterruptedException e) {            e.printStackTrace();        }    }    }

 

创建实现了 InvocationHandle 接口的 TimeHandler

      TimeHandle 类

package com.msym.jdkproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class TimeHandler implements InvocationHandler {    private Object target;        public void setTarget(Object target) {        this.target = target;    }        /*     * 参数:     *     proxy : 被代理类的对象     *     method : 代理对象需要被代理的方法(也就是需要被增强的方法)     *     args : 被增强方法需要的参数     * */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("我是增强的时间计算");        long startTime = System.currentTimeMillis();        //调用被代理对象的原始方法, 这里的返回值是原始方法的返回值, 如果有返回值则返回, 没有返回值则为null        Object obj = method.invoke(target);        long endTime = System.currentTimeMillis();        System.out.println("我是增强的时间计算,现在结束了, 时间: " + (endTime - startTime) + "毫秒");        //其实这里返回的是null, 因为被增强的方法move本身就没有返回值        return obj;    }}

测试类 :

[ 其中的四步就是实现动态代理的四个步骤 ]

package com.msym.jdkproxy;import java.lang.reflect.Proxy;public class Test {    @org.junit.Test    public void carProxyTest() {        // 1. 创建处理对象        TimeHandler h = new TimeHandler();        // 2. 创建需要被代理的对象(也就是需要被增强的类的对象)        Moveable m = new Car();        // 将被代理对象与处理对象绑定        h.setTarget(m);        /* 3. 创建代理类对象, 也就是增强后的对象         * Proxy.newProxyInstance(loader, interfaces, h)        * 参数解释:        *     loader : 被代理对象的类加载器         *     interfaces : 被代理对象实现的接口         *     h : 实现动态代理的Handle对象          * */        Moveable m1 = (Moveable) Proxy.newProxyInstance(m.getClass().getClassLoader(), m.getClass().getInterfaces(), h);        // 4. 调用方法, 这时的方法已经是被增强的方法了        m1.move();    }}

运行结果 :

技术分享

CGLIB动态代理实现步骤 :

  1. 创建一个类实现 MethodInterceptor接口, 实现 intercept方法
  2. 创建第一步中类的对象
  3. 获取代理类实例
  4. 通过代理类实例调用目标方法

举个栗子 : [ 还是之前的栗子, 给 Car类增强一个打日志的功能 ]

    Car类 : 没有实现任何接口或抽象类

package com.msym.cglibproxy;/** * 该类不用实现接口或者抽象类 * @author 码上猿梦 *  http://www.cnblogs.com/daimajun/ */public class Car {    public void move(){        System.out.println("我要开车了.....");    }}

    CglibProxy类 :

package com.msym.cglibproxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;/** *  * @author 码上猿梦 *  http://www.cnblogs.com/daimajun/ */public class CglibProxy implements MethodInterceptor {    //创建增强对象, 用于创建子类对象(也就是代理对象)    private Enhancer enhancer = new Enhancer();        public Object getProxy(Class<?> clazz){        //设置父类class        enhancer.setSuperclass(clazz);        //设置回调对象        enhancer.setCallback(this);        //创建代理对象实例(也就是子类对象)        return enhancer.create();    }        /**     * 拦截调用被代理对象的所有方法的方法     * 参数:     *     obj : 被代理的类的实例     *     m : 被代理对象中的方法(通过反射获取)     *     args : 代理对象方法需要的参数     *     proxy : 代理类的实例(也就是被代理类子类的实例对象)     */    @Override    public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {        //增强的功能:        System.out.println("日志开始记录了,,,,,");        /*    原本的功能:         *  因为是拦截调用父类的方法,所以就不会去调用父类的方法了,这里需要显示的调用父类的方法,         *     返回值是父类方法的返回值,和JDK代理一样,父类方法如果有返回值,你这里的res就是那个返回值,         *     如果父类方法没有返回值, 这里的res就是null         */        Object res = proxy.invokeSuper(obj, args);        //增强的功能:        System.out.println("日志记录结束了......");        return res;    }}

测试类 :

package com.msym.cglibproxy;/** *  * @author 码上猿梦 *  http://www.cnblogs.com/daimajun/ */public class Test {        @org.junit.Test    public void test(){        CglibProxy cglib = new CglibProxy();        //因为动态生成的代理对象是被代理类的子类, 所有可以强转        Car cat = (Car) cglib.getProxy(Car.class);        cat.move();    }}

运行结果 :

技术分享

 

 

 

JDK代理和CGLIB代理的区别 :

JDK代理 : 只能代理实现了接口的类, 没有实现接口的类不能使用 JDK动态代理

CGLIB代理 : 针对类来进行代理, 对指定目标类产生一个子类 , 通过方法拦截技术拦截所有对父类方法的调用, 在拦截方法中(也就是 intercept()方法中再次调用父类的方法, 同时在调用的前后添加若干功能代码 )

 

 

 

 

 

 

 

 

 

 

 

代理模式