意图
为其他对象提供一种代理以控制对这个对象的访问
代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而为客户和目标对象引入一定的间接性,正是这个间接性,给了代理对象很多的活动空间,代理对象可以在调用具体的目标对象前后,附加很多操作,从而实现新的功能或是扩展目标对象的功能,更狠的是,代理对象还可以不去创建和调用目标对象,也就是说,目标对象被完全代理掉了,或是被替换掉了。
功能
代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象过后,对客户端没有什么影响,就跟得到了真实对象一样来使用。
当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。
结构
- Proxy:代理对象:
a) 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
b) 保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象,可以控制对具体目标对象的访问,并可能负责创建和删除它。
c) 其他的功能依赖于代理的类型 - Subject:目标接口
定义RealSubject和Proxy的共用接口,这样就可以在任何使用具体目标对象的地方使用代理对象 -
RealSubject:具体的目标对象
具体的目标对象,真正实现目标接口要求的功能。
代理的分类
-
虚代理:
根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建; -
远程代理:
为一个对象在不同的地址空间提供局部代表,这个不同的地址空间可以是在本机,也可以在其它机器上,在Java里面最典型的就是RMI技术; -
Copy-on-Write代理:
在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支;
在实现Copy-on-write时必须对实现进行引用计数。拷贝代理仅会增加引用计数。只有当用户请求一个修改该实体的操作时,代理才会真正的拷贝它。在这种情况下,代理还必须减少实体的引用计数。当引用的数目为零时,这个实体将被删除。 -
保护代理:
控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问; -
智能指引:
在访问对象时执行一些附加操作,比如:
a) 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它;
b) 当第一次引用一个持久对象时,将它装入内存。
c) 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
Java中的代理
Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类和一个InvocationHandler的接口。
通常把前面自己实现的代理模式,称为Java的静态代理。这种实现方式有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活,而使用Java内建的对代理模式支持的功能来实现则没有这个问题。
通常把使用Java内建的对代理模式支持的功能来实现的代理称为Java的动态代理。动态代理跟静态代理相比,明显的变化是:静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法;而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。
注意:Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。如果要实现类的代理,可以使用cglib、Javassist。
相关模式
代理模式 VS 适配器模式
相同点:它们都为另一个对象提供间接性的访问,而且都是从自身以外的一个接口向这个对象转发请求。
不同点:但是从功能上,两个模式是不一样的。适配器模式主要用来解决接口之间不匹配的问题,它通常是为所适配的对象提供一个不同的接口;而代理模式会实现和目标对象相同的接口。代理模式 VS 装饰模式
相同点:实现上是类似的,都是在转调其它对象的前后执行一定的功能。但是它们的目的和功能都是不同的。
不同点:装饰模式的目的是为了让你不生成子类就可以给对象添加职责,也就是为了动态的增加功能;而代理模式的主要目的是控制对对象的访问。
示例
- 静态代理
Subject:
public abstract class Subject
{
public abstract void request();
}
RealSubject:
public class RealSubject extends Subject
{
public void request()
{
System.out.println("From real subject.");
}
}
ProxySubject:
public class ProxySubject extends Subject
{
private RealSubject realSubject; //代理角色内部引用了真实角色
public void request()
{
this.preRequest(); //在真实角色操作之前所附加的操作
if(null == realSubject)
{
realSubject = new RealSubject();
}
realSubject.request(); //真实角色所完成的事情
this.postRequest(); //在真实角色操作之后所附加的操作
}
private void preRequest()
{
System.out.println("pre request");
}
private void postRequest()
{
System.out.println("post request");
}
}
客户端使用:
public class Client
{
public static void main(String[] args)
{
Subject subject = new ProxySubject();
subject.request();
}
}
- 动态代理
Subject:
public interface Subject
{
public void request();
}
RealSubject:
public class RealSubject implements Subject
{
public void request()
{
System.out.println("From real subject!");
}
}
DynamicSubject:
/**
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
* 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
* 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后
* 加入自己的一些额外方法。
*
*/
public class DynamicSubject implements InvocationHandler
{
private Object sub;
public DynamicSubject(Object obj)
{
this.sub = obj;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("before calling: " + method);
method.invoke(sub, args);
System.out.println(args == null);
System.out.println("after calling: " + method);
return null;
}
}
注意:invoke的第一个参数Object proxy指的是动态代理的那个对象,也就是我们下面通过Proxy.newProxyInstance(...)方法构建的动态代理对象。
客户端使用:
public class Client
{
public static void main(String[] args)
{
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
Subject subject = (Subject) Proxy.newProxyInstance(classType
.getClassLoader(), realSubject.getClass().getInterfaces(),
handler);
subject.request();
System.out.println(subject.getClass());
}
}
① static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
这句代码生成的实例,既不是RealSubject实例,也不是DynamicSubject实例。生成的是运行期间动态所生成的实例。
② 所谓Dynamic Proxy是这样一种class:
它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。(Class的实例就可以是任何一个接口)当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler(InvocationHandler h),由它接管实际的工作
③ subject.request();
不管调用生成代理对象(subject只是个代理)的任何一个方法,流程都会立刻转换到了handler里的invoke方法。
所以我们在使用动态代理的时候,一般需要通过Proxy的静态方法来生成一个动态代理类,然后我们就可以使用这个动态代理类来替代真是的类了。
动态代理类可以实现真实类所实现的所有接口类,同时动态代理类的构建还需要我们传入一个InvocationHandler。InvocationHandler对象会真实替我们完成代理的操作,也就是说我们调用代理类的某个方法后,最终都会转到该对象中通过invoke方法来实现。
参考
《Head First 设计模式》
《设计模式:可复用面向对象软件的基础》
《研磨设计模式》
圣思园 Java SE