也被称为委托模式,代理模式为其他对象提供一种代理以控制对这个对象的访问。
目录
[TOC]
1、组成及优点
1.1 组成
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
1.2 优点
- 职责清晰:真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事务,通过后期代理完成非本职工作。
- 高扩展性:具体主题角色可以随时发生变化,只要它实现了接口,而不用关心它如何变化,只要接口没变,代理类可以在完全不做修改的情况下使用。
- 智能化:通过动态代理的方式实现在编码阶段不需要知道代理的对象。
- 扩展原功能,不侵入原代码。
- 普通代理模式,在该模式下调用者只知道代理而不用知道真实角色,屏蔽了真实角色的变更对高层模块的影响,在实际项目中,一般都是通过约定来禁止new一个真实的角色。
1.3 缺点
- 代理类必须实现接口,导致代理类太多;
- 一旦接口发生变更,代理类也必须同步更新,导致维护成本会比较高;
2、静态代理
1.1 情景模拟
秉承着通过代码段学习设计模式的的宗旨,我们通过生活中一个常见的情景作为一个栗子:小明同学通过代码的方式从国外购买一台 Mac
以下为根据此情况设计的 UML 图:
1.2 具体代码
1.2.1 定义的接口
/**
* Time: 2018/6/6 17:45
* 定义目标对象的接口方法
* 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
*/
public interface Subject {
public void buybuybuy();
}
1.2.2 目标类(被代理类)
/**
* Time: 2018/6/6 17:45
* 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
* 实现抽象目标对象的接口
* 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
*/
public class XiaoMing implements Subject {
private static final String TAG = "_XiaoMing";
@Override
public void buybuybuy() {
Log.e(TAG, "buybuybuy: 小明要买Mac");
}
}
1.2.3 代理类
/**
* Time: 2018/6/7 0007 10:58
* 真正进行操作的对象 代理对象
* 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法* 来实现抽象方法,并可以附加自己的操作。 */
public class ForginerBuyer implements Subject {
private static final String TAG = "_ForginerBuyer";
private Subject mSubject;
public ForginerBuyer(Subject mSubject) {
this.mSubject = mSubject;
}
@Override
public void buybuybuy() {
Log.e(TAG, "buybuybuy: 代购开始购买");
mSubject.buybuybuy();
Log.e(TAG, "buybuybuy: 代购结束购买");
}
}
1.2.4 客户端
public class BuyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buy);
//创建 XiaoMing 实例对象
Subject mXiaoMing = new XiaoMing();
//创建 ForginerBuyer 代购对象,并把 XiaoMing 实例对象传入
Subject mBuyer = new ForginerBuyer(mXiaoMing);
mBuyer.buybuybuy();
}
}
1.2.5 执行结果
E/_ForginerBuyer: buybuybuy: 代购开始购买
E/_XiaoMing: buybuybuy: 小明要买Mac
E/_ForginerBuyer: buybuybuy: 代购结束购买
1.2.6 小结
通过以上代码,稍微思考一下其实在平时静态代理十分的常见,就是把真正的对象包装一下成为一个新的类,通过访问这个新的类我们就可以真正的对目标类对象进行操作。但是我们需要让代理类和目标类发生关系 -- 把目标类对象传入代理类中。
但是难道我们在使用代理模式时必须要首先构建一个抽像对象吗?我个人觉得不是必须的。即使代理类和目标类没有共同的方法,只要把目标类的实例对象传入代理类中,在代理类中调用目标类的相应方法,那么也能够完成代理的最终目的。
3、动态代理
动态代理和静态代理的不同:在程序运行前,代理类不存在,而是在程序运行时通过反射机制生成。
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。动态代理类:在程序运行时,运用反射机制动态创建而成。
还是通过上面 小明海外代购一台 Mac 的栗子来说明 动态代理的具体实现。
3.1 具体代码
3.1.1 定义接口
/**
* Time: 2018/6/6 17:45
* 定义目标对象的接口方法
* 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
*/
public interface Subject {
public void buybuybuy();
}
3.1.2 目标类
/**
* Time: 2018/6/6 17:45
* 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
* 实现抽象目标对象的接口
* 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
*/
public class XiaoMing implements Subject {
private static final String TAG = "_XiaoMing";
@Override
public void buybuybuy() {
Log.e(TAG, "buybuybuy: 小明要买Mac");
}
}
3.1.3 声明调用处理器类
/** 作用
* 1. 生成 动态代理对象
* 2. 指定 代理对象运行目标对象方法时需要完成的 具体任务
*/
public class DynamicProxy implements InvocationHandler {
private static final String TAG = "DynamicProxy";
private Object ProxyObject;
public Object newProxyInstance(Object ProxyObject){
this.ProxyObject =ProxyObject;
return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
ProxyObject.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Log.e(TAG, "invoke: 代购出门了" );
Object result = null;
// 通过Java反射机制调用目标对象方法
result = method.invoke(ProxyObject, args);
return result;
}
}
3.1.4 通过动态代理对象,调用目标对象的方法
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
// 1. 创建调用处理器类对象
DynamicProxy DynamicProxy = new DynamicProxy();
// 2. 创建目标对象对象
XiaoMing mXiaoMing = new XiaoMing();
// 3. 创建动态代理类 & 对象:通过调用处理器类对 newProxyInstance()
// 传入上述目标对象对象
Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
// 4. 通过调用动态代理对象方法从而调用目标对象方法
// 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
mSubject.buybuybuy();
}
}
注:以上代码参考 Carson_Ho的简书
3.1.5 执行结果
E/_DynamicProxy: invoke: 代购出门了
E/_XiaoMing: buybuybuy: 小明要买Mac
3.2 代码解析
在静态代理模式中,代理类是通过我们我们自己编辑的,根据动态代理模式的特点:在程序运行期生成代理类,那么动态代理类是怎么生成的呢?怎么通过调用动态代理类的实例来调用目标类的方法?下面我们通过具体代码来说明这两个问题
3.2.1 生成代理类
很明显,在具体的调用中我们是通过以下代码生成代理类的:
Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
跳转到具体代码(终点关注标注(重要代码)的代码段)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// Android-changed: sm is always null
// final SecurityManager sm = System.getSecurityManager();
// if (sm != null) {
// checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
// }
/*
* (重要代码)
* Look up or generate the designated proxy class.
* 查找或者生成代理类,正是在此处我们通过代码生成了代理类,
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
// }
//(重要代码)
//通过反射获得动态代理的构造器对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
// Android-changed: Removed AccessController.doPrivileged
cons.setAccessible(true);
}
//(重要代码)
//通过反射获得代理类的具体对象实例
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
但是具体是怎么生成动态代理类的呢,我们具体参考Class<?> cl = getProxyClass0(loader, intfs);
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache
为 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
我们需要关注的是 ProxyClassFactory
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
....
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
巴拉巴拉一大堆,我不想看这是什么鬼,反正最后来到了了
generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray);
,那我们来看一个generateProxy(...)
@FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
这是一个 Native
方法,具体为生成代理类,我在网上查看一下资料时看到有些博客上可以调用相应的 api 生成具体的可以看到的代理类,但是我在具体的 demo 中不能进行此操作,故此代码跟踪完毕。
3.3 与静态代理相比的优点
当委托类特别多时,即在每个委托类都对应着一套规则(即每一个委托类都对应着一个抽象对象),那么相应的如果使用静态代理模式,那么就需要实现多个相应的代理类,这样代理类个数会比较多。但是如果使用动态代理模式,此时在生成 InvocationHandler
相应的实例时传入不同的委托类实例对象,那么则可以实现同事代理多个委托类的功能。
DynamicProxy DynamicProxy = new DynamicProxy();
XiaoMing mXiaoMing = new XiaoMing();
Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
mSubject.buybuybuy();
XiaoHong mXiaoHong= new XiaoHong();
Subject2 mSubject2 = (Subject2) DynamicProxy.newProxyInstance(mXiaoHong);
mSubject2 .buybuybuy2();
即在委托类比较多时,建议使用动态代理模式
优秀博客:
代理模式
Java 动态代理
动态代理相对于静态代理的优势
代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析
网友评论