文章首发于【博客园-陈树义】,点击跳转到原文深入浅出Java动态代理
代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位。代理模式从类型上来说,可以分为静态代理和动态代理两种类型。
今天我将用非常简单易懂的例子向大家介绍动态代理的两种类型,接着重点介绍动态代理的两种实现方式(Java 动态代理和 CGLib 动态代理),最后深入剖析这两种实现方式的异同,最后说说动态代理在我们周边框架中的应用。
在开始之前,我们先假设这样一个场景:有一个蛋糕店,它们都是使用蛋糕机来做蛋糕的,而且不同种类的蛋糕由不同的蛋糕机来做,这样就有:水果蛋糕机、巧克力蛋糕机等。这个场景用 Java 语言描述就是下面这样:
//做蛋糕的机器 public interface CakeMachine{ void makeCake(); } //专门做水果蛋糕的机器 class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("Making a fruit cake..."); } } //专门做巧克力蛋糕的机器 public class ChocolateCakeMachine implements CakeMachine{ public void makeCake() { System.out.printf("making a Chocolate Cake..."); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { new FruitCakeMachine().makeCake(); //making a Fruit Cake... new ChocolateCakeMachine().makeCake(); //making a Chocolate Cake... } }上面的代码抽象出了一个 CakeMachine 接口,有各种蛋糕机(FruitCakeMachine、ChocolateCakeMachine 等)实现了该接口,最后蛋糕店(CakeShop)直接利用这些蛋糕机做蛋糕。
这样的一个例子真实地描述了实际生活中的场景。但生活中的场景往往是复杂多变的,假设这个时候来了一个顾客,他想要一个水果蛋糕,但他特别喜欢杏仁,希望在水果蛋糕上加上一层杏仁。这时候我们应该怎么做呢?
因为我们的蛋糕机只能做水果蛋糕(程序设定好了),没办法做杏仁水果蛋糕。最简单的办法是直接修改水果蛋糕机的程序,做一台能做杏仁水果蛋糕的蛋糕机。这种方式对应的代码修改也很简单,直接在原来的代码上进行修改,生成一台专门做杏仁水果蛋糕的机器就好了,修改后的 FruitCakeMachien 类应该是这样子:
//专门做水果蛋糕的机器,并且加上一层杏仁 class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("making a Fruit Cake..."); System.out.println("adding apricot..."); } }虽然上面这种方式实现了我们的业务需求。但是仔细想一想,在现实生活中如果我们遇到这样的一个需求,我们不可能因为一个顾客的特殊需求就去修改一台蛋糕机的硬件程序,这样成本太高!而且从代码实现角度上来说,这种方式从代码上不是很优雅,修改了原来的代码。根据代码圈中「对修改封闭、对扩展开放」的思想,我们在尝试满足新的业务需求的时候应该尽量少修改原来的代码,而是在原来的代码上进行拓展。
那我们究竟应该怎么做更加合适一些呢?我们肯定是直接用水果蛋糕机做一个蛋糕,然后再人工撒上一层杏仁啦。这其实就对应了即使模式中的代理模式,在这个业务场景中,服务员(代理人)跟顾客说没问题,可以做水果杏仁蛋糕,于是服务员充当了一个代理的角色,先让水果蛋糕机做出了水果蛋糕,之后再往上面撒了一层杏仁。在这个例子中,实际做事情的还是水果蛋糕机,服务员(撒杏仁的人)只是充当了一个代理的角色。
下面我们就来试着实现这样一个代理模式的设计。我们需要做的,其实就是设计一个代理类(FruitCakeMachineProxy),这个代理类就相当于那个撒上一层杏仁的人,之后让蛋糕店直接调用即可代理类去实现即可。
//水果蛋糕机代理 public class FruitCakeMachineProxy implements CakeMachine{ private CakeMachine cakeMachine; public FruitCakeMachineProxy(CakeMachine cakeMachine) { this.cakeMachine = cakeMachine; } public void makeCake() { cakeMachine.makeCake(); System.out.println("adding apricot..."); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { FruitCakeMachine fruitCakeMachine = new FruitCakeMachine(); FruitCakeMachineProxy fruitCakeMachineProxy = new FruitCakeMachineProxy(fruitCakeMachine); fruitCakeMachineProxy.makeCake(); //making a Fruit Cake... adding apricot... } }通过代理实现这样的业务场景,这样我们就不需要在原来的类上进行修改,从而使得代码更加优雅,拓展性更强。如果下次客人喜欢葡萄干水果蛋糕了了,那可以再写一个 CurrantCakeMachineProxy 类来撒上一层葡萄干,原来的代码也不会被修改。上面说的这种业务场景就是代理模式的实际应用,准确地说这种是静态代理。