十一、代理模式
11.1 代理的概述
-
代理模式的作用
为其他对象提供一种代理以控制对目标对象的访问。某些情况下客户不想或不能直接引用另一个对象,而代理对象可在客户端和目标对象间起到中介作用。
-
代理模式的分类
-
优点:不需要修改目标对象就实现了功能的增加。
缺点:真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。如果事先并不知道真实角色则无法使用;
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀。
-
Java动态代理类位于java.lang.reflect包下,主要涉及到两个类:InvocationHandler接口和
Proxy类;
-
InvocationHandler接口仅定义了一个方法:
public object invoke(Object obj,Method method, Object[] args){}
//obj一般是指代理类
//method是被代理的方法
//args为该方法的参数数组
//这个抽象方法在代理类中动态实现
- Proxy类即为动态代理类,主要方法包括:
protected Proxy(InvocationHandler h)
//构造函数,用于给内部的h赋值
static Object newProxyInstance(ClassLoader loader, Class[ ] interfaces, InvocationHandler h) {}
//返回代理类的一个实例
//loader是类装载器
//interfaces是真实类所拥有的全部接口的数组
static Class getProxyClass (ClassLoader loader, Class[] interfaces) {}
//获得一个代理类
-
代理模式一般涉及到的角色
-
抽象角色:真实对象和代理对象的共同接口;
-
真实角色:真实对象,最终要引用的对象;
-
代理角色:
-
内部含有对真实对象的引用,从而可以操作真实对象;
-
提供与真实对象相同的接口以便在任何时刻代替真实对象;
-
可在执行真实对象操作前后附加其他操作,相当于对真实对象进行封装。
小贴士:
必须明确一个动态代理类可适用多个抽象角色和真实角色!
-
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 动态代理的使用
-
创建一个实现接口InvocationHandler的类;
-
在invoke方法内通过反射调用真实对象的方法,并添加附加操作;
-
创建被代理的类以及接口;
-
通过Proxy类的newProxyInstance() 创建一个代理;
-
必须传入一个InvocationHandler对象;
-
通过代理调用方法。
/**
* 抽象角色
*/
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方法)