1 概述
代理(Proxy)模式是一种常见的设计模式,其具体含义是给某一对象提供代理对象,代理对象和具体对象实现共同接口,并且代理对象中维护了具体对象的引用,外界对具体对象的操作需要通过代理对象调用具体对象的方法来实现。 代理模式的优点在于减小了客户端和具体对象间的耦合性并且满足了OCP(Open Close Priciple,开闭原则)。
代理模式在实际生活中的应用也是非常常见的,比如卖烟和卖酒的都是通过代理商来进行买卖的,而不是顾客直接去工厂购买。在web技术中,代理模式也有具体的实践,比如在打开一个网站时可以发现,文字一般都是立即显示,而图片资源都是慢慢加载的(当图片较大时,这种现象更明显),这里就用到了代理模式,其中代理对象调用具体对象在后头默默下载,并且代理对象负责前台的信息处理等。
2 实现方式
2.1 举例说明
代理模式可以通过现实生活中的代理商卖商品来进行说明,这里通过汽车代理商卖汽车来进行阐述代理模式的实现过程。首先上代码:
public class ProxyDemo {
public static void main(String[] args) {
CarProxy carProxy = new CarProxy();
carProxy.sell();
}
}
interface Sellable {
void sell();
}
class CarFactory implements Sellable {
@Override
public void sell() {
System.out.println("sell a car");
}
}
class CarProxy implements Sellable {
private CarFactory carFactory = new CarFactory();
@Override
public void sell() {
carFactory.sell();
}
}
这里,主要有三种角色,即:(1)共同接口Sellable,其要求实现类必须实现sell方法;(2)具体对象CarFactory,其造出来的车可以卖;(3)代理类CarProxy,其维护了一个CarFactory类的引用。当客户端调用CarProxy的sell方法时,其实调用的是具体对象carFactory的sell方法。
但是有人可能会问,为什么需要CarProxy这个代理类,看起来它也没有干什么其它的事啊?这就是代理模式的设计理念了,主要包括以下两点:
(1)代理类的出现降低了客户端和具体对象的耦合度,甚至客户端不必知道具体对象的存在。这和我们现实生活的场景也是一致的,人们买车的时候没有必要联系大众,奔驰这种汽车生产商,而只需去汽车4S店(相当于代理商的角色)买车即可。
(2)在软件开发的过程中,有个重要的原则就是开闭原则(Open Close Principle),即对扩展保持开放,对修改保持关闭。具体到上面的例子,当卖汽车这个操作需要扩展时(比如,在卖车时需要进行车主的资格认证和核实),不可能在具体的CarFactory这个类中修改(有时候这个类是jar包形式提供的,也不可能进行修改),那么可以通过CarProxy这个代理类进行扩展。其实,这也说明了代理类有权限控制的作用,可以在调用具体对象的方法时进行权限检查等操作。
2.2 UML类图
代理模式的UML类图比较简单,这里直接给出上面例子的UML类图描述:
3 动态代理
上面的例子是代理模式的静态实现,所谓静态代理就是需要手动写或者用工具生成一个代理类,也就是说这个代理类在程序运行前就已经存在。这有时候会有点麻烦,导致应用不灵活。为此,Java提供了动态代理机制,将代理类的指定延迟到程序运行时由JVM来实现,其实现的关键是利用了Proxy类和InvocationHandler接口。
还是以上面的卖汽车的例子进行说明,这里不再指定代理类CarProxy了,而是通过Proxy类动态的创建一个代理类,其关键在于对InvocationHandler接口的具体实现DynamicProxy类传入了一个CarFactory对象。而DynamicProxy类和之前的CarProxy类的区别在于,DynamicProxy无需知道CarFactory对象的任何信息,也就是说DynamicProxy类是一个通用的类。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
InvocationHandler handler = new DynamicProxy(carFactory);
Sellable sellable = (Sellable) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
carFactory.getClass().getInterfaces(), handler);
sellable.sell();
}
}
interface Sellable {
void sell();
}
class CarFactory implements Sellable {
@Override
public void sell() {
System.out.println("sell a car");
}
}
class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// sth. can be done before
Object result = method.invoke(this.obj, args);
// sth. can be done after
return result;
}
}
Java动态代理的经典实现就是Spring框架的AOP(切面编程),其实在DynamicProxy类中的invoke方法中,调用具体的方法之前和之后都可以干很多事,比如权限控制,用户拦截,运行时间统计等。
4 总结
总结来说,代理模式的主要思想是通过代理对象实现调用方和具体对象之间的解耦,有利于代码的扩展,可以实现诸如权限控制等功能。而动态代理是将代理类延迟到程序运行期间来生成,其经典的实现是Spring框架的AOP。