代理模式是我们在看源码中经常会看到的设计模式,像 Retrofit
、ActivityManagerService
等都使用到了代理模式。
那什么时候需要使用到代理模式呢?
1. 代理模式的概念
代理模式(Proxy), 为其他对象提供一种代理以控制对这个对象的访问。
结构型的设计模式,在所有的结构型的设计模式中,都会有不少代理模式的影子,代理模式是为了形成中介隔离,这里有点像体现迪米特法则的 中介者模式,但是他们还是有一些区别的,这一点学完就知道了。
2 UML图
来看下UML类图:
代理模式的结构是非常简单的。来看下他们的实现。
Subject
类,定义了被代理类和代理类的共用接口,这样就在任何使用RealSubject
的地方都可以使用 Proxy
类:
public abstract class Subject {
public abstract void request();
}
其次是 RealSubject
类,它是真实逻辑所在的实体:
class RealSubject extends Subject {
@Override
public void request() {
System.out.println("真实的请求");
}
}
最后是 Proxy
代理类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体:
public class Proxy extends Subject {
RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.request();
}
}
最后在客户端代码中使用代理类来做:
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
3. 静态代理和动态代理
在Java中,代理模式可以分为静态代理和动态代理。
3. 1 静态代理
静态代理就和上面的模板代码一样,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说我们在代码运行前,代理类的class编译文件就已经存在。
3.2 动态代理
动态代理和静态代理相反,通过反射机制动态地生成代理者对象,也就说在Coding阶段根本就不需要知道代理谁,代理谁我们会在执行阶段决定,而Java也给我们提供了一个便捷的动态代理接口 InvocationHandler
,实现该接口需要重写其调用方法 invoke()
:
class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
在我们使用代理时,需要对它进行些完善:
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 {
//调用被代理对象的方法
return method.invoke(obj, args);
}
}
这时,再来看看客户端:
public static void main(String[] args) {
// 构造一个实体类
Subject subject = new RealSubject();
// 构造一个动态代理
DynamicProxy proxy = new DynamicProxy(subject);
// 获取被代理类RealSubject的ClassLoader
ClassLoader loader = subject.getClass().getClassLoader();
// 根据动态代理构造一个代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(loader, new Class[]{Subject.class}, proxy);
// 用代理对象来执行方法
proxySubject.request();
}
可以看到动态代理通过一个代理类来代理N多个被代理类,其实质是对代理者与被代理者进行解耦,是两者之间没有直接的耦合关系。
4. 代理模式的应用
在Andorid中,使用代理模式的地方还挺多的
- Binder本身就是代理模式的实现,Bundle数据从一个进程传输到另一个进程时,它会封装成像
BpBinder
、BBinder
这样的类来供其他进程来访问本进程的东西。
像AIDL
它使用了Binder,它是一种静态代理,可以看我之前总结的文章来学习 AIDL是如何体现Binder的:从AIDL来看Binder的通信原理(基于Andorid8.0) - Android7.0及之前源码,Activity与AMS进行通信,使用的就是代理类:
ActivityManagerProxy
- 封装类本身就是代理模式的灵活应用
5. 小结
首先是代理模式的使用场景:
- 远程代理
为某个对象在不同的内存地址空间提供局部代理。使系统可以将Server部分的实现给隐藏。
譬如Binder
AIDL的 跨进程通信 - 虚拟代理
使用一个代理对象表示一个十分消耗资源的对象并在真正需要时才创建 - 保护代理
使用代理控制对原始对象访问,该类型的代理被用于原始对象有不同访问权限的情况。 - 智能引用
在访问原始对象时,执行一些自己的附加操作并对指向原始对象的引用计数。
譬如封装类。
其次是代理者模式和中介者模式的区别:
- 中介者模式的两个类是可以互相通过中介者进行访问的,而代理模式只能用代理类访问被代理类,而被代理类它是不能够访问代理类对象的。
- 关于代理类和被代理类,静态代理是1对1的关系,动态代理是1对多的关系,而中介者模式是多对多的关系。
关于静态代理和动态代理的区别:
- 静态代理在code阶段必须要要知道被代理的对象有什么功能,通过这个功能来编写代理类。
- 动态代理在code阶段不需要知道谁是被代理类,只需要写好通用的代理类,就能在需要的时候去代理任何需要被代理的对象。
两者的区别就只是在code层上而已。可以按照喜好、具体场景来使用,并无严格的优劣之分。