业务场景的复杂度往往千变万化,如果有另外一个客人,他也想在巧克力蛋糕上撒一层杏仁,那我们岂不是也要再写一个代理类让他做同样的一件事情。如果有客人想在抹茶蛋糕上撒一层杏仁,有客人想在五仁蛋糕上撒一层杏仁……那我们岂不是要写无数个代理类?
其实在 Java 中早已经有了针对这种情况而设计的一个接口,专门用来解决类似的问题,它就是动态代理 —— InvocationHandler。
动态代理与静态代理的区别是静态代理只能针对特定一种类型(某种蛋糕机)做某种代理动作(撒杏仁),而动态代理则可以对所有类型(所有蛋糕机)做某种代理动作(撒杏仁)。
接下来我们针对这个业务场景做一个代码的抽象实现。首先我们分析一下可以知道这种场景的共同点是希望在各种蛋糕上都做「撒一层杏仁」的动作,所以我们就做一个杏仁动态代理(ApricotHandler)。
//杏仁动态代理 public class ApricotHandler implements InvocationHandler{ private Object object; public ApricotHandler(Object object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(object, args); //调用真正的蛋糕机做蛋糕 System.out.println("adding apricot..."); return result; } }撒杏仁的代理写完之后,我们直接让蛋糕店开工:
public class CakeShop { public static void main(String[] args) { //水果蛋糕撒一层杏仁 CakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler fruitCakeApricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine fruitCakeProxy = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), fruitCakeApricotHandler); fruitCakeProxy.makeCake(); //巧克力蛋糕撒一层杏仁 CakeMachine chocolateCakeMachine = new ChocolateCakeMachine(); ApricotHandler chocolateCakeApricotHandler = new ApricotHandler(chocolateCakeMachine); CakeMachine chocolateCakeProxy = (CakeMachine) Proxy.newProxyInstance(chocolateCakeMachine.getClass().getClassLoader(), chocolateCakeMachine.getClass().getInterfaces(), chocolateCakeApricotHandler); chocolateCakeProxy.makeCake(); } }输出结果为:
making a Fruit Cake... adding apricot... making a Chocolate Cake... adding apricot...从输出结果可以知道,这与我们想要的结果是一致的。与静态代理相比,动态代理具有更加的普适性,能减少更多重复的代码。试想这个场景如果使用静态代理的话,我们需要对每一种类型的蛋糕机都写一个代理类(FruitCakeMachineProxy、ChocolateCakeMachineProxy、MatchaCakeMachineProxy等)。但是如果使用动态代理的话,我们只需要写一个通用的撒杏仁代理类(ApricotHandler)就可以直接完成所有操作了。直接省去了写 FruitCakeMachineProxy、ChocolateCakeMachineProxy、MatchaCakeMachineProxy 的功夫,极大地提高了效率。
看到这里,大家应该清楚为什么有了静态代理之后,还需要有动态代理了吧。静态代理只能针对某一种类型的实现(蛋糕机)进行操作,如果要针对所有类型的实现(所有蛋糕机)都进行同样的操作,那就必须要动态代理出马了。
如何使用动态代理?参照上面的例子,我们可以知道要实现动态代理需要做两方面的工作。
首先需要新建一个类,并且这个类必须实现 InvocationHandler 接口。
//杏仁动态代理 public class ApricotHandler implements InvocationHandler{ private Object object; public ApricotHandler(Object object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(object, args); //调用真正的蛋糕机做蛋糕 System.out.println("adding apricot..."); return result; } }在调用的时候使用 Proxy.newProxyInstance() 方法生成代理类。
public class CakeShop { public static void main(String[] args) { //水果蛋糕撒一层杏仁 CakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler fruitCakeApricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine fruitCakeProxy = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), fruitCakeApricotHandler); fruitCakeProxy.makeCake(); }最后直接使用生成的代理类调用相关的方法即可。
动态代理的几种实现方式动态代理其实指的是一种设计模式概念,指的是通过代理来做一些通用的事情,常见的应用有权限系统、日志系统等,都用到了动态代理。
而 Java 动态代理只是动态代理的一种实现方式而已,动态代理还有另外一种实现方式,即 CGLib(Code Generation Library)。