说到 Java JDK 的静态代理或动态代理,听起来就像是很高大上的技术。那么,今天我们就用一种通俗易懂的方式来分析分析,看看能不能揭开她神秘的面纱···
一、代理概念
为某个对象提供一个代理,以控制对这个对象的访问。这句话咋一看,有点抽象,那就举个生活中比较通俗点的例子,就是假设你到了结婚年龄了,像找个对象,但你是个比较内向的人,生活中认识的女士并不多,而且你感觉都不合适,于是乎就把找女朋友这个神圣的任务委托给一个你非常要好而且结交广泛的朋友来帮你。那么,你这个朋友就是代理人,相当于我们这里说的代理类,而你自己就是委托人,相当于我们这里说的委托类。
但是,我们这里的代理类和委托类跟上面举的例子稍有不同,有其自己的特点。我们这里的代理类和委托类都有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
如下面这张图所示:
稍微解释一下这张图,图中的“Subject”实际上是个接口,他有一个“doSomething()”方法。“ProxySubject”就是我们的代理类,他实现了“Subject”接口。“RealSubject”就是我们的委托类,他也实现了“Subject”接口。“Client”就是我们的调用者即客户端,可以简单理解为某个应用程序,他持有一个“Subject”接口对象,这样一来,既可以直接使用“RealSubject”委托类,也可以使用“ProxySubject”代理类来替代。
看到这里,估计有人就要开始问了,本来很简单的一件事,这样折腾有啥好处呀?好处还是蛮多的,这里我试举其中一个例子,请想想看,在我们利用 JDBC 操作数据库时,经常要做的一个“组合动作”即“打开链接 -> 启动数据库事务 -> 执行增删改查-> 提交事务 -> 关闭链接”,每次“增删改查”执行前都要“打开链接 -> 启动数据库事务”,执行后都要“提交事务 -> 关闭链接”。如果我们把“增删改查”的方法只放在我们这里的“RealSubject”委托类中,而执行前后的动作放在“ProxySubject”代理类中,事情是不是瞬间变得美好起来了呢?
另,Java 中的代理,又可以分为静态代理和动态代理。
二、静态代理
由程序员手动编写或者工具生成的代理类。 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就已经确定了。
举个不恰当的例子,假设我们用程序模拟某知名餐饮连锁店的服务生,规定服务生的两个行为“迎客”和“送客”。起初,我们规定每个服务生迎客时必须说“您好,欢迎光临!”,送客时必须说“请慢走,欢迎再次光临!”。为了日后对此规定进行改进,因此,我们把服务生这个角色抽象出一个“IWaiter”接口,如下所示:
/**
* 服务员接口
*
* @author ZHULX
*
*/
public interface IWaiter {
/**
* 欢迎顾客
*/
public void sayWelcome();
/**
* 欢送顾客
*/
public void sayGoodbye();
}
按照起初的规定,我们很容易完成以下实现类:
/**
* 服务员实现类
*
* @author ZHULX
*
*/
public class WaiterImpl implements IWaiter {
/**
* 欢迎顾客实现方法
*/
@Override
public void sayWelcome() {
System.out.println("******您好,欢迎光临! ******");
}
/**
* 欢送顾客实现方法
*/
@Override
public void sayGoodbye() {
System.out.println("******请慢走,欢迎再次光临! ******");
}
}
过一段时间后,总部对此规定提出更高要求,并规定迎客时必须“向顾客行90°度鞠躬礼”,送客时必须“向顾客行45°度鞠躬礼”。但是,负责开发的总工程师意识到这个规定未来可能还会有别得要求,但是“您好,欢迎光临”这个最基本的要求是永远不变的,于是,要求不能改变原有“WaiterImpl”类的源码,但是却要实现新的规定要求。为此,我们的代理类正好派上用场了:
/**
* 服务员静态代理类
*
* @author ZHULX
*
*/
public class StaticProxyWaiter implements IWaiter {
// 持有被代理的接口
private IWaiter waiter;
/**
* 构造方法
*
* @param waiter
* 被代理的接口
*/
publicStaticProxyWaiter(IWaiterwaiter) {
this.waiter =waiter;
}
/**
* 欢迎顾客代理方法
*/
@Override
public void sayWelcome() {
System.out.println("******向顾客行90°度鞠躬礼 ******");
this.waiter.sayWelcome();
}
/**
* 欢送顾客代理方法
*/
@Override
public void sayGoodbye() {
System.out.println("******向顾客行45°度鞠躬礼 ******");
this.waiter.sayGoodbye();
}
}
最后,我们设计一个测试类来测试一下我们的设计是否符合我们的要求,代码如下所示:
/**
* 测试类
*
* @author ZHULX
*
*/
public class Test {
public static void main(String[] args) {
// 被代理的对象
IWaiter waiter = new WaiterImpl();
System.out.println("未代理前的执行结果:");
waiter.sayWelcome();
waiter.sayGoodbye();
// 代理对象
IWaiter proxyWaiter = new StaticProxyWaiter(waiter);
// 执行代理对象的方法
System.out.println("\n代理后的执行结果:");
proxyWaiter.sayWelcome();
proxyWaiter.sayGoodbye();
}
}
如果一切正常,输出应该如下所示:
未代理前的执行结果:
****** 您好,欢迎光临! ******
****** 您慢走,欢迎再次光临! ******
代理后的执行结果:
****** 向顾客行90°度鞠躬礼 ******
****** 您好,欢迎光临! ******
****** 向顾客行45°度鞠躬礼 ******
****** 您慢走,欢迎再次光临! ******
可以看到,我们的代理类工作一切正常。
今天就写到这里,待续~