模式的秘密---代理模式
java设计模式23种设计模式视频(第13~15章代理模式 复合模式 桥接模式)
第一推荐:轻松学,Java 中的代理模式及动态代理
第二推荐:10分钟看懂动态代理设计模式
为其他对象提供一种代理,以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或增加额外的服务。
举例:火车购票,可以去直接去火车站也可以去代售点,这个代售点就可以称之为火车站的代理。而代售点除了卖火车票外,可能会有自己额外的服务,比如电话预约。同时,代售点是不能退火车票的,要退的话只能去火车站。这个代售点,少了一些功能服务(退票),增加了一些额外服务(电话预约)。
远程代理:为不同地理的对象,提供局域网代表对象。类似于客户端-服务器的模式。
举例:我有一家连锁店,我想实时了解各个门店的销售信息,我们可以通过远程代理来构造各个门店的监视器,来随时报告各个门店的销售和库存信息。
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。
举例:我们在浏览一个新闻的时候,会有文字和图片,如果网络情况不好,我们可以使用虚拟代理,用一个本地的图片暂时替代我们要加载的图片,当图片获取成功后再展示出来,如图二所示。这就是虚拟代理的一个简单应用。
图一:
图二:
保护代理:权限控制。例如,有些网站,只有注册登陆后才能发帖、评论。
智能引用代理:刚才举的火车票代售点的例子。
静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类。
我们用代码来实现车行驶。
2.1.2.1、常规方式
Moveable.java
public interface Moveable {
void move();
}
Car.java
public class Car implements Moveable {
/**
* 如果不使用代理模式的话,我们如果想记录车行驶的时间,会采用如下方式
*/
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
// 实现开车
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Client.java
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
Car car = new Car();
car.move();
}
}
打印结果为:
2.1.2.2、通过继承的方式实现
Car.java
public class Car implements Moveable {
@Override
public void move() {
// 实现开车
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Car2.java
public class Car2 extends Car {
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
// 调用父类 Car 的方法 move()
super.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Client.java
public class Client {
public static void main(String[] args) {
// 使用继承的方式
Moveable moveable = new Car2();
moveable.move();
}
}
打印结果为:
2.1.2.3、通过聚合的方式实现
聚合:简单理解就是在一个类中调用另一个类的对象。
Car3.java
public class Car3 implements Moveable {
private Car car;
// 通过构造方法 把 Car 传进来
public Car3(Car car) {
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
car.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Clien.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
Car car = new Car();
Car3 car3 = new Car3(car);
car3.move();
}
}
打印结果不变。
以上我们通过继承和聚合两种方式来实现静态代理,那么哪种方式更适合静态代理呢?
我们分析一下,先来说继承这种方式,现在如果需求发生变化,我们就得新建一个子类,如下图:
很显然,这种方式会随着功能的增加而变得越来越臃肿,因此这种方式应该被排除!
那么,聚合的方式呢?
CarTimeProxy.java
public class CarTimeProxy implements Moveable {
private Moveable moveable;
public CarTimeProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
moveable.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
CarLogProxy.java
public class CarLogProxy implements Moveable {
private Moveable moveable;
public CarLogProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
System.out.println("日志开始...");
moveable.move();
System.out.println("日志结束...");
}
}
Client.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
// Car car = new Car();
// Car3 car3 = new Car3(car);
// car3.move();
//先记录日志,再记录时间
Car car = new Car();
CarTimeProxy carTimeProxy = new CarTimeProxy(car);
CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
carLogProxy.move();
}
}
打印结果为:
如果我们想先记录时间再记录日志呢?很简单:
Client.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
// Car car = new Car();
// Car3 car3 = new Car3(car);
// car3.move();
//先记录日志,再记录时间
// Car car = new Car();
// CarTimeProxy carTimeProxy = new CarTimeProxy(car);
// CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
// carLogProxy.move();
//先记录时间,再记录日志
Car car = new Car();
CarLogProxy carLogProxy = new CarLogProxy(car);
CarTimeProxy carTimeProxy = new CarTimeProxy(carLogProxy);
carTimeProxy.move();
}
}
打印结果为:
可以看到,使用聚合的方式会比使用继承的方式灵活很多。
那么,接下来,如果我们需要给火车加上行驶时间和日志的记录呢?我们是否需要再新建两个代理类?有没有更简单的方法呢?
那么,有没有一种方法能够动态产生代理,实现对不同类、不同方法的代理呢?下面我们来了解一下动态代理。
我们先来了解一下JDK的动态代理:
图一:
图二:
下面通过代码来了解一下:
TimeHandler.java
/**
* InvocationHandler 事务处理器
*/
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
/**
* @param proxy 被代理的对象
* @param method 被代理的对象的方法
* @param args 被代理的对象的方法的参数
* @return 所调用方法的返回值
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
method.invoke(target);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
return null;
}
}
Test.java
public class Test {
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class> cls = car.getClass();
/**
* newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
* loader: 被代理类的类加载器
* interfaces: 我们要实现的接口
* h:事务处理器
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
m.move();
}
}
打印结果:
总结:
动态代理实现步骤:
作业,实现时间记录和日志记录:
public class Test {
public static void main(String[] args) {
// timeTest();
// logTest();
timeAndLogTest();
}
private static void timeAndLogTest() {
Car car = new Car();
InvocationHandler timeHandler = new TimeHandler(car);
Class> cls = car.getClass();
Moveable moveable = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), timeHandler);
InvocationHandler logHandler = new LogHandler(moveable);
Class> class2 = moveable.getClass();
Moveable moveable2 = (Moveable) Proxy.newProxyInstance(class2.getClassLoader(),
class2.getInterfaces(), logHandler);
moveable2.move();
}
private static void logTest() {
Car car = new Car();
InvocationHandler logHandler = new LogHandler(car);
Class> cls = car.getClass();
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), logHandler);
m.move();
}
private static void timeTest() {
Car car = new Car();
InvocationHandler timeHandler = new TimeHandler(car);
Class> cls = car.getClass();
/**
* newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
* loader: 被代理类的类加载器
* interfaces: 我们要实现的接口
* h:事务处理器
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), timeHandler);
m.move();
}
}