一、概述
什么是代理?就比如说我们朋友圈中有专门做海外代购朋友,我们自己不能出国去买,就只能通过代购的方式(网购也算),所以他就是代理的角色;打官司需要请律师,而律师也是一个代理的角色,全权交给律师来进行诉讼;我们委托别人代购的东西到了,没时间去取快递,就可以让朋友代理取,朋友也是扮演的代理的角色。通过简单的例子我们就明白什么是代理模式。代理模式提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能。
代理模式的关键点是代理对象与目标对象(被代理对象)。代理对象是对目标对象的扩展,并会调用目标对象。代理模式也分为静态代理和动态代理。
二、使用
1、静态代理
静态代理通常是定义一个接口或者父类,目标对象与代理对象一起实现相同的接口或者是继承相同父类。这里说通常,是因为我们要对代理对象进行一些约束,比如朋友代理取快递,我们只需要让他代理取就行,而不需要拆快递,或者使用我们的东西;当然如果快递有损坏,或者一些异常的情况,我们也需要委托朋友代理退货的操作,因此代理的操作可能会有很多,所以写一个共同的接口或者父类更加的直观,也更好的理解静态代理。
下面我们还是以律师诉讼为例,来做具体说明。首先我们写一个诉讼接口,里面包含各种流程,当事人和代理律师都需要实现其中的方法。
/**
* 诉讼接口
*/
public interface LawSuit {
String TAG = "XXX";
//开场
void start();
//陈述
void statement();
//提供证据
void provideEvidence();
//结束
void end();
}
其次是当事人实现了诉讼接口,在抽象的方法中实现当事人自己的逻辑。
/**
* 当事人
*/
public class Litigant implements LawSuit {
@Override
public void start() {
Log.e(TAG, "当事人说:各位好!法官大人好!");
}
@Override
public void statement() {
Log.e(TAG, "当事人说:被告恶意碰瓷");
}
@Override
public void provideEvidence() {
Log.e(TAG, "当事人说:我有监控证据");
}
@Override
public void end() {
Log.e(TAG, "当事人说:我已经说完了");
}
}
接下来就是代理律师了,也实现了诉讼接口,并且持有了当事人的引用,可以看到,在实现的方法中调用了当事人的相关方法。
/**
* 代理律师
*/
public class Lawyer implements LawSuit {
private Litigant litigant;
public Lawyer(Litigant litigant){
this.litigant = litigant;
}
@Override
public void start() {
litigant.start();
Log.e(TAG, "代理律师说:各位好!法官大人好!");
}
@Override
public void statement() {
Log.e(TAG, "代理律师说:我方开始陈述");
litigant.statement();
Log.e(TAG, "代理律师说:我方陈述完毕");
}
@Override
public void provideEvidence() {
Log.e(TAG, "代理律师说:我方开始提供证据");
litigant.provideEvidence();
Log.e(TAG, "代理律师说:我方已经提供完所有证据");
}
@Override
public void end() {
litigant.end();
Log.e(TAG, "代理律师说:我方已经说完了");
}
}
最后进行测试,打印输出如下图所示。可以在代理律师的方法中,我们可以看到,在调用当事人类中的方法时,可以在之前或者之后扩展自己的逻辑。
Litigant litigant = new Litigant();
Lawyer lawyer = new Lawyer(litigant);
lawyer.start();
lawyer.statement();
lawyer.provideEvidence();
lawyer.end();

小结静态代理,通过上面的代码测试,使用静态代理其实很简单。调用代理类的方法时,其实最后真正执行的还是目标对象的方法,代理类可以在不修改目标对象的功能下,可以对目标对象功能的扩展。当然有一些缺陷就是,当共有接口发生改变时,代理对象和目标都会受到影响;当代理类过多时,也会导致类的数量增加,不容易维护。所以为了解决这些问题,我们就需要用到动态代理。
2、动态代理
动态代理不需要使用共有接口,而代理对象的生成需要用到JDK中的java.lang.reflect.InvocationHandler接口,以及java.lang.reflect.Proxy这个类中的newProxyInstance静态方法。
那么接下来就用代码说明。首先我们需要创建一个类来实现InvocationHandler接口,这个接口只有一个invoke方法。在类中定义了一个获取代理对象的方法,这个方法中用到了newProxyInstance这个关键,下面分别对newProxyInstance方法中的几个参数说明一下:
- ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的;
- Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型;
- InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
然后对InvocationHandler接口中的invoke方法参数说明一下:
- Object proxy:代理对象,就是getProxy方法生成的对象;
- Method method:当前调用的方法;
- Object[] args:调用当前方法传入的参数。
/**
* 动态代理
*/
public class DynamicProxy implements InvocationHandler {
private static final String TAG = "XXX";
private Object target;
/**
* 建立代理对象和目标对象的代理关系,并返回代理对象
* @param target 目标对象
* @return 代理对象
*/
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("statement".equals(method.getName())){
Log.e(TAG, "代理律师说:我方开始陈述");
}else if("provideEvidence".equals(method.getName())){
Log.e(TAG, "代理律师说:我方开始提供证据");
}
//以上就是在调用目标对象之前的逻辑
Object obj = method.invoke(target,args);
//以下就是调用目标对象之后的逻辑
if("start".equals(method.getName())){
Log.e(TAG, "代理律师说:各位好!法官大人好!");
}else if("statement".equals(method.getName())){
Log.e(TAG, "代理律师说:我方陈述完毕");
}else if("provideEvidence".equals(method.getName())){
Log.e(TAG, "代理律师说:我方已经提供完所有证据");
}else if("end".equals(method.getName())){
Log.e(TAG, "代理律师说:我方已经说完了");
}
return obj;
}
}
在invoke方法中,我们可以看到调用Object obj = method.invoke(target,args);语句,这里有反射相关的使用,关于反射的用法可以参照其他的博文,这里不再过多说明。当执行到这条语句时,实际上是调用目标对象的方法(也就是我们的当事人类中的方法)
,在调用目标对象方法前后,可以扩展其他的逻辑。这里只是简单的判断了方法名称,根据执行不同的方法,使得代理对象打印不同的信息。最后进行测试,得到打印输出如下所示。
DynamicProxy dynamicProxy = new DynamicProxy();
LawSuit lawSuit = (LawSuit) dynamicProxy.getProxy(new Litigant());
lawSuit.start();
lawSuit.statement();
lawSuit.provideEvidence();
lawSuit.end();

小结动态代理,代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。动态代理的核心就是InvocationHandler接口和Proxy.newProxyInstance静态方法,使用起来也并不复杂。
三、总结
代理模式也是我们在平常用的较多的一种模式。当然相对于动态代理来说,静态代理要用的更多一些,使用起来也很简单方便。在Android中代理模式是用到很多的,比如跨进程通信、Activity的启动这些都会涉及到的服务端(service)和客户端(client)进行交互,当然使用起来肯定就比我们的例子要复杂的多的多,有兴趣的也可以看看这一系列的文章。
github地址:github.com/leewell5717…