代理模式

代理模式的定义 :

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

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

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

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

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

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

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

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

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

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

 

实现代理的方式有两种 :

  • 静态代理
  • 动态代理

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

image

静态代理有种方法 :

  • 继承
  • 聚合

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

 

动态代理也有两种方法 :

  • 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;
/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 */
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("我要开始打日志了,,,,");
        //调用被代理对象的原始方法, 这里的返回值是原始方法的返回值, 如果有返回值则返回, 没有返回值则为null
        Object obj = method.invoke(target);
        System.out.println("日志结束了......");
        //其实这里返回的是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();
    }
}

运行结果 :

image

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();
    }

}

运行结果 :

image

 

 

JDK 代理和 CGLIB 代理的区别 :

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

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

 

 

 

 

 

 

 

 

 

 

 

1

posted @ 2017-08-06 12:05  码上猿梦  阅读(268)  评论(0编辑  收藏  举报