一、简介
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。这里我举一个买车票的例子。通常我们我们买车票需要去车站买,但是这样会很麻烦,可能要坐很久的车去车站,然后在排队买票。但是如果我们去一个卖车票的代理点买车票可能就会省去这么多的事情。这样车票售卖处就代理你购买车票。
二、详解
1.代理模式的UML类图
从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。
2.代理模式的实现
代理模式可以有两种实现的方式,一种是静态代理,另一种是各大框架都喜欢的动态代理。下面我们主要讲解一下这两种代理模式
1)静态代理
静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态代理。
代码实现:
下面我们就用具体的代码来实现上面这个买车票的静态代理。
首先创建一个售票服务的接口,它有售票咨询和退票的业务可以供客户选择。
public interface TicketService {
//售票
void sellTicket();
//咨询
void Consultation();
//退票
void ReturnTicket();
}
然后,创建一个售票服务接口实现类,就好比是车站。
public class Station implements TicketService {
@Override
public void sellTicket() {
System.out.println("售票");
}
@Override
public void Consultation() {
System.out.println("咨询");
}
@Override
public void ReturnTicket() {
System.out.println("退票");
}
}
然后创建一个代理售票点
public class StationProxy implements TicketService {
private Station station;
public StationProxy(Station station){
this.station = station;
}
@Override
public void sellTicket() {
System.out.println("欢迎使用车票代售点进行购票,每张票将会收取5元手续费!");
station.sellTicket();
System.out.println("欢迎下次光临");
}
@Override
public void Consultation() {
System.out.println("欢迎咨询,咨询不收取费用");
station.Consultation();
System.out.println("欢迎下次光临");
}
@Override
public void ReturnTicket() {
System.out.println("欢迎使用车票代售点进行退票,每张票将会收取5元手续费!");
station.ReturnTicket();
System.out.println("欢迎下次光临");
}
}
创建购买车票的角色,去代理点完成购买车票的需求
public class ProxyTest {
public static void main(String[] args) {
Station station = new Station();
StationProxy stationProxy = new StationProxy(station);
stationProxy.sellTicket();
}
}
可以看到这个购买车票的客户是去的车票代理点,直接购买车票,那代理点能否帮助他正常购买到车票呢?请看下面的执行结果
通过上面的代理代码,我们可以看出代理模式的特点,代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
2)动态代理
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(Proxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
动态代理是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy
,通过固定的规则生成。
代理步骤:
(1)实现被代理类及其实现的接口,
(2)定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。
(3)调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。
(4)通过该代理实例调用方法。
//1. 接口
public interface Moveable {
void move() throws Exception;
}
//1. 被代理实现类
public class Car implements Moveable {
public void move() throws Exception {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中…");
}
}
//2.事务处理器
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
/**
* 参数:
*proxy 被代理的对象
*method 被代理对象的方法
*args 方法的参数
*Object 方法返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶…");
method.invoke(target, args);
long stopTime = System.currentTimeMillis();
System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception{
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
*loader 类加载器
*interfaces 实现接口
*h InvocationHandler
*/
Moveable m = (Moveable)
Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
m.move();
}
在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。