代理模式(Proxy)
Proxy模式,也称代理模式,是经典设计模式中的一种结构型模式,其定义是为其他对象提供一种代理以控制对这个对象的访问,代理对象起到了中介作用,不涉及功能服务,亦可增加额外的服务。通俗来说就是,为其他对象提供代理对象,以控制对这个对象的访问。
代理模式的分类
- 远程代理:为不同的地理对象提供局域网代表对象。典型的设计有:C/S架构属于远程代理的缩影
- 虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候再创建。典型设计:经常我们看到很多APP在加载图片的时候,会先加载一个默认的图片,等真正的图片加载完了之后再显示出来,这样非常的友好。
- 智能引用代理:提供对目标对象的额外的服务。典型设计:现实场景中随处可见,我们的火车、汽车票代售处、代购等等都是属于代理模式的范畴。
- 保护代理:控制用户的访问权限。典型设计:就像我们的公众号的文章留言功能,只有你这个用户关注了该公众号之后才能留言,否则你就只能浏览不能留言。
静态代理
代理和被代理对象在代理之前都是被确定的,他们都实现相同的接口或者是继承相同的抽象类,这里一张经典的图
上面这张图我们可以看出:ProxySubject和RealSbuject都是继承了Subject,Client请求Subjct的doSomething()方法,本应该是调用RealSubject中的doSomething()方法,但是我们实际看到的却是调用的ProxySubject方法中的doSomething()方法,在ProxySubject方法中的doSomething()方法中再去调用RealSubject中的doSomething()方法。我们实际看到的是ProxySubject方法中的doSomething()方法,这样就完成了代理。
简单实例
1.共同实现的接口
public interface Subject{
public void doSomeThing();
}
2.被代理对象,逻辑执行者
public class RealSubject implements Subject{
@Override
public void doSomeThing() {
//do some things
}
}
3.代理类
我们在构造方法里面创建了一个RealSubject的实现类对象,在调用Proxy的doSomething()方法的时候,实际是调用的RealSubject对象的的doSomething()方法,但是经常我们还会附加一些逻辑在里面比如我们需要打印一些Log日志,直接这样加就可以了:
public class Proxy implements Subject{
private Subject subject = null;
public Proxy(){
subject = new RealSubject();
}
@Override
public void doSomeThing() {
log.d("TAG","代理开始...")
subject.doSomeThing();
log.d("TAG","代理开结束...")
}
}
这样的好处就是正在实现类的逻辑不用做任何改变即可实现。
动态代理
动态代理有以下特点:
- 代理对象,不需要实现接口
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
JDK中InvocationHandler接口与Proxy类
InvocationHandler类
InvocationHandler 是一个接口,官方文档解释说:每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler是个接口,只有一个抽象方法 invoke(Object proxy, Method method, Object[] args),四个参数的意义:
- param proxy 代理类
- param method 被代理的方法
- param args 被代理方法的参数
- return 方法的返回值
Proxy类
JDK通过 Proxy 的静态方法 newProxyInstance 动态地创建代理,该方法在Java中的声明如下
/**
* @description
* @author rico
* @created 2017年7月3日 下午3:16:49
* @param loader 类加载器
* @param interfaces 目标类所实现的接口
* @param h InvocationHandler 实例
* @return
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
事实上,Proxy 动态产生的代理对象调用目标方法时,代理对象会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
简单示例
1.接口
同样地,首先得有一个接口,通用的接口是代理模式实现的基础。
public interface ISubject {
//出行
public void doSomething();
}
2.被代理角色
然后,我们要有一个真正的实现这个ISubject接口的类,以便代理。
public class RealSubject implements ISubject {
private static final String TAG = "RealSubject";
@Override
public void doSomething() {
Log.e(TAG, "doSomething: 执行中");
}
}
3.代理类
在动态代理中,代理类及其实例是程序自动生成的,因此我们不需要手动去创建代理类。在Java的动态代理机制中,InvocationHandler(Interface)接口和Proxy(Class)类是实现我们动态代理所必须用到的。事实上,Proxy通过使用InvocationHandler对象生成具体的代理代理对象,下面我们看一下对InvocationHandler接口的实现:
/**
* Created by Scorpio on 2018/7/9.
* 该类经常称之为事务处理类
*/
public class DynamicProxy implements InvocationHandler {
private static final String TAG = "DynamicProxy";
// 被代理对象
private Object target;
public DynamicProxy(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 {
Log.e(target.getClass().getName(), "开始日志。。。");
method.invoke(target);
Log.e(target.getClass().getName(), "结束日志。。。");
return null;
}
}
4.客户端
在实现了InvocationHandler接口后,我们就可以创建代理对象了。在Java的动态代理机制中,我们使用Proxy类的静态方法newProxyInstance创建代理对象,如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "===================RealSubject========================= ");
RealSubject realSubject = new RealSubject();
DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
ISubject proxyInstance = (ISubject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), dynamicProxy);
proxyInstance.doSomething();
}
}
输出
07-10 00:07:30.284 30241-30241/com.lt.proxy E/MainActivity: ===================RealSubject=========================
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 开始日志。。。
07-10 00:07:30.285 30241-30241/com.lt.proxy E/RealSubject: doSomething: 执行中
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 结束日志。。。
总结
优点:
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
- 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了保护目标对象的作用。
缺点:
- 由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。