1.代理模式概述
用一个类来代理另一个类的功能,为访问某个类的对象提供一种代理以控制访问。并且在此基础上增强原来的功能。
举例:
- windows系统上的快捷方式,当我们需要运行一个软件时,不需要找到其安装位置双击.exe文件,而直接访问桌面上的快捷方式即可。
- 火车站买票,我们不需要自己去火车站购买,可以去代售点购买,不过代价是收取一定的服务费。
在代理模式中有以下的几个角色:
- 抽象主题角色:提供原始方法的接口
- 真实主题角色:实现了的抽象主题角色的接口方法
- 代理角色:同样实现了原始方法接口,并且聚合了真实主题角色。
接下来考虑这样一个场景,我们需要通过桌面快捷方式访问一个应用程序。那么分析之后可知,与上面角色一一对应,有以下的几个类和接口。
- Application:对应上面的抽象主题类,提供访问它的接口。
- ConcreteApplication:实现了访问应用程序的接口。
- ProxyApplication:代理角色
UML类图:
public interface Application {
public void getRequest();
}
public class ConcreteApplication implements Application{
@Override
public void getRequest() {
System.out.println("从磁盘路径xxxx中读取.exe文件----打开文件");
}
}
public class ProxyApplication implements Application{
private ConcreteApplication concreteApplication;
public ProxyApplication(){
concreteApplication=new ConcreteApplication();
}
@Override
public void getRequest() {
System.out.println("正在点击桌面快捷方式---正在重定向路径---");
concreteApplication.getRequest();
}
}
public class Client {
public static void main(String[] args) {
Application application=new ProxyApplication();
application.getRequest();
//
正在点击桌面快捷方式---正在重定向路径---
从磁盘路径xxxx中读取.exe文件----打开文件
}
}
2.扩展
上述的写法被称为静态代理,在编译期间就已经确定。
实际上java语言为我们提供了另一种称为动态代理的模式,代理类对象的方法在运行时确定下来。
public class DynamicProxy {
private ConcreteApplication concreteApplication=new ConcreteApplication();
public Application getProxyObject(){
ClassLoader loader = concreteApplication.getClass().getClassLoader();
Class<?>[] interfaces = concreteApplication.getClass().getInterfaces();
Object application=Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在点击桌面快捷方式---正在重定向路径");
return method.invoke(concreteApplication, args);
}
});
return (Application) application;
}
}
实现原理是这样的:
底层会创建一个实现了Application接口的类,并且实现了getRequest()方法,而代理类的getRequest()方法中又调用了InvocationHandler重写的方法。
这种方法的好处是,当抽象代理角色中有很多的接口时,静态代理要求全部都实现,而动态代理则是根据需要动态完成。
cglib代替,上述情况是在有抽象主题类接口的情况下才能使用的。在没有接口的情况下,cglibjar包为我们提供了实现方法,继承现有的具体主题类。
public class CglibProxy implements MethodInterceptor {
private ConcreteApplication concreteApplication=new ConcreteApplication();
public ConcreteApplication getProxyObject(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(ConcreteApplication.class);
enhancer.setCallback(this);
ConcreteApplication object=(ConcreteApplication) enhancer.create();
return object;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("正在点击桌面快捷方式---正在重定向路径---");
Object invoke = method.invoke(concreteApplication, objects);
return invoke;
}
}
3.总结
注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。