模式的秘密——代理模式
一、代理模式的概念及分类
代理模式的概念:为其它对象提供一种代理,以控制对这个对象的访问。
分类:远程代理、智能引用代理、保护代理、虚拟代理。
远程代理:为不同地理的对象提供局域网代表对象。
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。
保护代理:控制对一个对象的访问权限。
智能引用代理:提供对目标对象额外的一些服务。
二、举个栗子
我们以智能引用代理为例,分别采用静态代理和动态代理的方式实现。
静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类。
假设一辆小车有一个行驶的方法,然后我们通过代理实现这个行驶的方法,同时增加记录行驶时间的方法。
首先定义接口Moveable
public interface Moveable{
void move();
}
然后定义Car并实现Moveable接口
public class Car implements Moveable {
@Override
public void move() {
try {
System.out.println("汽车行驶中...");
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1、通过继承的方式定义Car2
public class Car2 extends Car {
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
super.move();
long endTime = System.currentTimeMillis();
System.out.println("汽车停止行驶,行驶时间:" + (endTime - startTime)+"毫秒");
}
}
通过上面的代码我们定义了Car2并继承了Car,在Car2的move方法中我们调用了父类的move方法。
2、通过聚合的方式
public class Car3 implements Moveable {
private Car car;
public Car3(Car car) {
super();
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
car.move();
long endTime = System.currentTimeMillis();
System.out.println("汽车停止行驶,行驶时间:" + (endTime - startTime) + "毫秒");
}
}
我们通过继承和聚合的方式分别实现了汽车行驶过程中记录行驶时间的方法,如果需要进一步的拓展,比如:记录日志、权限等方法,通过继承的方式就需要定义Car4、Car5等,显然这种做法不值得推荐。
例如:我们要实现汽车行驶过程中记录日志和行驶时间的功能。
1、记录行驶时间的代理
public class CarTimeProxy implements Moveable {
private Moveable m;
public CarTimeProxy(Moveable m) {
super();
this.m = m;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
m.move();
long endTime = System.currentTimeMillis();
System.out.println("汽车停止行驶,行驶时间:" + (endTime - startTime) + "毫秒");
}
}
2、记录行驶日志的代理
public class CarLogProxy implements Moveable {
private Moveable m;
public CarLogProxy(Moveable m) {
super();
this.m = m;
}
@Override
public void move() {
System.out.println("日志开始");
m.move();
System.out.println("日志结束");
}
}
3、测试,先记录日志再记录时间
public static void main(String[] args) {
Carcar = new Car();
CarTimeProxyctp = new CarTimeProxy(car);
CarLogProxyclp = new CarLogProxy(ctp);
clp.move();
}
通过将记录时间的实例传递给记录日志的实例,实现了先记录日志再记录时间的操作。如果想实现先记录时间在记录日志,将两个实例对象的传递交换一下就能够实现想要的操作。
动态代理
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1) Interface InvocationHandler:该接口中仅定义了一个方法
public objectinvoke(Object obj, Methond method, Object[] args)在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。
(2) Proxy:该类即为动态代理类
static ObjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在接口中声明过的方法)
还是上面的栗子,我们使用动态代理的方式实现。
1、定义TimeHandler实现InvocationHandler接口
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
method.invoke(target);
long endTime = System.currentTimeMillis();
System.out.println("汽车停止行驶,行驶时间:" + (endTime - startTime) + "毫秒");
return null;
}
}
2、测试
public static void main(String[] args) {
// TODO Auto-generated method stub
Carcar = new Car();
InvocationHandlerh = new TimeHandler(car);
Class<?>cls = car.getClass();
Moveablem = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), h);
m.move();
}
三、代理模式优缺点
优点
1、代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
2、代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了保护目标对象的作用。
缺点
1、由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
四、代理模式总结
1、代理模式是通过使用引用代理对象来访问真实对象,在这里代理对象充当用于连接客户端和真实对象的中介。
2、代理模式主要用于远程代理、虚拟代理和保护代理,其中保护代理可以进行访问权限控制。