十一、代理模式

11.1 代理的概述

  • 代理模式的作用

    为其他对象提供一种代理以控制对目标对象的访问。某些情况下客户不想或不能直接引用另一个对象,而代理对象可在客户端和目标对象间起到中介作用。

  • 代理模式的分类

  • 静态代理

    优点:不需要修改目标对象就实现了功能的增加。

    缺点:真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。如果事先并不知道真实角色则无法使用;

    一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀。

  • 动态代理

  1. Java动态代理类位于java.lang.reflect包下,主要涉及到两个类:InvocationHandler接口和

    Proxy类;

  2. InvocationHandler接口仅定义了一个方法:

public object invoke(Object obj,Method method, Object[] args){}
//obj一般是指代理类
//method是被代理的方法
//args为该方法的参数数组
//这个抽象方法在代理类中动态实现
  1. Proxy类即为动态代理类,主要方法包括:
protected Proxy(InvocationHandler h) 
//构造函数,用于给内部的h赋值

static Object newProxyInstance(ClassLoader loader, Class[ ] interfaces, InvocationHandler h) {}
//返回代理类的一个实例
//loader是类装载器
//interfaces是真实类所拥有的全部接口的数组

static Class getProxyClass (ClassLoader loader, Class[] interfaces) {}
//获得一个代理类
  • 代理模式一般涉及到的角色

  • 抽象角色:真实对象和代理对象的共同接口;

  • 真实角色:真实对象,最终要引用的对象;

  • 代理角色:

    1. 内部含有对真实对象的引用,从而可以操作真实对象;

    2. 提供与真实对象相同的接口以便在任何时刻代替真实对象;

    3. 可在执行真实对象操作前后附加其他操作,相当于对真实对象进行封装。

    小贴士:

    必须明确一个动态代理类可适用多个抽象角色和真实角色!

11.2 静态代理的使用

/**
 * 抽象角色
 */
public interface USB {
    /**
     * 启动功能
     */
    void start();
    /**
     * 工作
     */
    void service();
    int size();
}

/**
 * 真实角色
 */
public class KeyBoard implements USB {
    @Override
    public void start() {
        System.out.println("键盘插入到电脑的USB接口中...");
    }

    @Override
    public void service() {
        System.out.println("键盘正在工作...");
    }

    @Override
    public int size() {
        return 80;
    }
}

/**
 * 代理角色
 */
public class UsbProxy implements USB {
    /**
     * 真实角色
     */
    KeyBoard keyBoard = new KeyBoard();
    @Override
    public void start() {
        System.out.println("日志:正在调用start方法");
        keyBoard.start();
        System.out.println("日志:调用start方法结束");
    }
    @Override
    public void service() {
        System.out.println("正在调用service方法");
        keyBoard.service();
    }
    @Override
    public int size() {
        System.out.println("正在调用size方法");
        int r = keyBoard.size();
        if (r >= 70){
            return 0;
        }
        System.out.println("size方法调用结束");
        return 0;
    }
}

/**
 * 测试类
 */
public class test {
    public static void main(String[] args) {
        UsbProxy usbProxy = new UsbProxy();
        /**
       	 * 无返回值类型的方法只可以调用不可以输出!
         */
        usbProxy.start();
        usbProxy.service();
        usbProxy.size();
        System.out.println(usbProxy.service());//错误
        System.out.println(usbProxy.size());//正确
    }
}

11.3 动态代理的使用

  1. 创建一个实现接口InvocationHandler的类;

  2. 在invoke方法内通过反射调用真实对象的方法,并添加附加操作;

  3. 创建被代理的类以及接口;

  4. 通过Proxy类的newProxyInstance() 创建一个代理;

  5. 必须传入一个InvocationHandler对象;

  6. 通过代理调用方法。

/**
 * 抽象角色
 */
public interface USB {
    /**
     * 启动功能
     */
    void start();
    /**
     * 工作
     */
    void service();
    int size();
    int length(int wd);
}

/**
 * 真实角色
 */
public class KeyBoard implements USB {
    @Override
    public void start() {
        System.out.println("键盘插入到电脑的USB接口中...");
    }
    @Override
    public void service() {
        System.out.println("键盘正在工作...");
    }
    @Override
    public int size() {
        return 80;
    }
    @Override
    public int length(int wd) {
        return 0;
    }
}

/**
 * 真实角色
 */
public class Mouse implements USB {
    @Override
    public void start() {
        System.out.println("鼠标识别中...");
    }
    @Override
    public void service() {
        System.out.println("鼠标工作中...");
    }
    @Override
    public int size() {
        System.out.println("鼠标温度...");
        return 0;
    }
    @Override
    public int length(int wd) {
        return 123;
    }
}

/**
 * 动态代理
 */
public class LogHandler implements InvocationHandler {
    //要代理的对象
    Object object;

    /**
     * 获取动态代理的方法(JDK内部帮我们获取目标对象的各种信息,并负责自动调用...)
     */
    public Object getProxy() {
        //返回的是一个动态代理对象,是在运行时动态创建的。
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("目标对象:"+proxy.getClass());
        System.out.println("目标对象-方法:"+method.getName());
        System.out.println("目标对象-参数:"+ Arrays.toString(args));
        Object result = method.invoke(object,args);
        System.out.println("目标对象返回值:"+result);
        return result;
    }
    /**
     * 传入真实角色
     * @param object
     */
    public LogHandler(Object object) {
        this.object = object;
    }
    public LogHandler() {
    }
}

/**
 * 测试类
 */
public class test {
    public static void main(String[] args) {
        /*KeyBoard keyBoard = new KeyBoard();
        //调用动态代理类,传入目标对象(真实角色)
        LogHandler logHandler = new LogHandler(keyBoard);
        USB usb = (USB) logHandler.getProxy();
        usb.start();*/

        Mouse mouse = new Mouse();
        LogHandler logHandler = new LogHandler(mouse);
        USB usb = (USB) logHandler.getProxy();
        usb.length(111);
    }
}

小贴士:

LogHandler类不是动态代理类!!

newProxyInstance()方法返回的才是动态代理对象,是在运行时动态创建的!

11.4 两种代理模式的区别

  • 静态代理类确实存在,动态代理类在运行期动态生成

  • 一个真实角色必须对应一个静态代理角色,而动态代理大大减少了代理类的数量

  • 动态代理类不会作实质性的工作,在生成它的实例时必须提供一个handler,由它接管实际的工作(会自动执行handler的invoke方法)