大道理上讲代理是一种软件设计模式,目的是但愿能作到代码重用。具体上讲,代理这种设计模式是经过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就比如 商户---->明星经纪人(代理)---->明星这种模式。咱们能够不经过直接与明星对话的状况下,而经过明星经纪人(代理)与其产生间接对话。node
代理能够看做是对调用目标的一个包装,这样咱们对目标代码的调用不是直接发生的,而是经过代理对象来完成的。数据库
(1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,咱们在工做中有时会接手不少前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时咱们就能够经过代理对类进行加强。编程
(2)咱们在使用RPC框架的时候,框架自己并不能提早知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用经过动态代理的方式来创建一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。设计模式
(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。数组
咱们根据加载被代理类的时机不一样,将代理分为静态代理和动态代理。若是咱们在代码编译时就肯定了被代理的类是哪个,那么就能够直接使用静态代理;若是不能肯定,那么可使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,好比RPC框架和Spring AOP机制。缓存
咱们先假设这样一个场景:有一个蛋糕店,它们卖的蛋糕都是用蛋糕机作的,并且不一样种类的蛋糕由不一样的蛋糕机来作,这样就有:水果蛋糕机、巧克力蛋糕机等。它们卖的面包片也是用面包机作的,一样不一样种类的面包片也是由不一样的面包机来作,这样就有:葡萄干面包机、红豆面包机等。这个场景用 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 interface BreadMachine { void makeBread(); } //专门作红豆面包的机器 public class RedBeanBreadMachine implements BreadMachine { public void makeBread() { System.out.println("making red bean bread...."); } } //专门作葡萄干面包的机器 public class CurrantBreadMachine implements BreadMachine{ public void makeBread() { System.out.println("making currant bread..."); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { new FruitCakeMachine().makeCake(); new ChocolateCakeMachine().makeCake(); new RedBeanBreadMachine().makeBread(); new CurrantBreadMachine().makeBread(); } }
上面的代码抽象出了一个 CakeMachine 接口和 BreadMachine 接口,有各类蛋糕机(FruitCakeMachine、ChocolateCakeMachine 等)实现了 CakeMachine 接口,有各类面包机(RedBeanBreadMachine、CurrantBreadMachine 等)实现了 BreadMachine 接口,最后蛋糕店(CakeShop)直接利用这些蛋糕机作蛋糕。框架
这样的一个例子真实地描述了实际生活中的场景。但生活中的场景每每是复杂多变的,假设这个时候来了一个顾客,他想要一个水果蛋糕,但他特别喜欢杏仁,但愿在水果蛋糕上加上一层杏仁。这时候咱们应该怎么作呢?ide
由于咱们的蛋糕机只能作水果蛋糕(程序设定好了),没办法作杏仁水果蛋糕。最简单的办法是直接修改水果蛋糕机的程序,作一台能作杏仁水果蛋糕的蛋糕机。这种方式对应的代码修改也很简单,直接在原来的代码上进行修改,生成一台专门作杏仁水果蛋糕的机器就行了,修改后的 FruitCakeMachien 类应该是这样子:函数
//专门作水果蛋糕的机器,而且加上一层杏仁 class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("making a Fruit Cake..."); System.out.println("adding apricot..."); } }
虽然上面这种方式实现了咱们的业务需求。可是仔细想想,在现实生活中若是咱们遇到这样的一个需求,咱们不可能由于一个顾客的特殊需求就去修改一台蛋糕机的硬件程序,这样成本过高!并且从代码实现角度上来讲,这种方式从代码上不是很优雅,修改了原来的代码。根据代码圈中「对修改封闭、对扩展开放」的思想,咱们在尝试知足新的业务需求的时候应该尽可能少修改原来的代码,而是在原来的代码上进行拓展。
那咱们究竟应该怎么作更加合适一些呢?咱们确定是直接用水果蛋糕机作一个蛋糕,而后再人工撒上一层杏仁啦。咱们须要作的,其实就是设计一个杏仁代理类(ApricotCakeProxy),这个代理类就完成撒杏仁这个动做,以后让蛋糕店直接调用便可代理类去实现便可。
//杏仁蛋糕代理 public class ApricotCakeProxy implements CakeMachine{ private CakeMachine cakeMachine; public ApricotCakeProxy(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(); ApricotCakeProxy apricotProxy = new ApricotCakeProxy(fruitCakeMachine); apricotProxy.makeCake(); apricotProxy = new ApricotCakeProxy(new ChocolateCakeMachine()); apricotProxy.makeCake(); } }
这其实就对应了代理模式中的代理模式,虽然调用的是 ApricotCakeProxy 类的方法,但实际上真正作蛋糕的是 FruitCakeMachine 类。ApricotCakeProxy 类只是在 FruitCakeMachine 作出蛋糕后,撒上一层杏仁而已。并且经过代理,咱们不只能够给水果蛋糕撒上一层杏仁,还能够给巧克力蛋糕、五仁蛋糕等撒上一层杏仁。只要它是蛋糕(实现了 CakeMachine 接口),那么咱们就能够给这个蛋糕撒上杏仁。
经过代理实现这样的业务场景,这样咱们就不须要在原来的类上进行修改,从而使得代码更加优雅,拓展性更强。若是下次客人喜欢葡萄干水果蛋糕了了,那能够再写一个 CurrantCakeProxy 类来撒上一层葡萄干,原来的代码也不会被修改。上面说的这种业务场景就是代理模式的实际应用,准确地说这种是静态代理。
业务场景的复杂度每每变幻无穷,若是这个特别喜欢杏仁的客人,他也想在面包上撒一层杏仁,那咱们怎么办?咱们可以使用以前写的 ApricotCakeProxy 代理类么?不行,由于 ApricotCakeProxy 里规定了只能为蛋糕(实现了 CakeMachine 接口)的实体作代理。这种状况下,咱们只能再写一个能够为全部面包加杏仁的代理类:ApricotBreadProxy。
//杏仁面包代理 public class ApricotBreadProxy implements BreadMachine{ private BreadMachine breadMachine; public ApricotBreadProxy(BreadMachine breadMachine) { this.breadMachine = breadMachine; } public void makeBread() { breadMachine.makeBread(); System.out.println("adding apricot..."); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { //能够给各类各样的面包加上杏仁 RedBeanBreadMachine redBeanBreadMachine = new RedBeanBreadMachine(); ApricotBreadProxy apricotBreadProxy = new ApricotBreadProxy(redBeanBreadMachine); apricotBreadProxy.makeBread(); CurrantBreadMachine currantBreadMachine = new CurrantBreadMachine(); apricotBreadProxy = new ApricotBreadProxy(currantBreadMachine); apricotBreadProxy.makeBread(); } }
最终的结果为:
咱们能够看到咱们也成功地作出了客人想要的杏仁红豆面包、杏仁葡萄干面包。
对于客人来讲,他确定但愿咱们全部的产品都有一层杏仁,这样客人最喜欢了。为了知足客人的需求,那若是咱们的产品有 100 种(饼干、酸奶等),咱们是否是得写 100 个代理类呢?有没有一种方式可让咱们只写一次实现(撒杏仁的实现),可是任何类型的产品(蛋糕、面包、饼干、酸奶等)均可以使用呢?其实在 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) { //动态代理(能够同时给蛋糕、面包等加杏仁) //给蛋糕加上杏仁 FruitCakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler apricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine cakeMachine = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), apricotHandler); cakeMachine.makeCake(); //给面包加上杏仁 RedBeanBreadMachine redBeanBreadMachine = new RedBeanBreadMachine(); apricotHandler = new ApricotHandler(redBeanBreadMachine); BreadMachine breadMachine = (BreadMachine) Proxy.newProxyInstance(redBeanBreadMachine.getClass().getClassLoader(), redBeanBreadMachine.getClass().getInterfaces(), apricotHandler); breadMachine.makeBread(); } }
输出结果为:
从输出结果能够知道,这与咱们想要的结果是一致的。与静态代理相比,动态代理具备更加的普适性,能减小更多重复的代码。试想这个场景若是使用静态代理的话,咱们须要对每一种类型的蛋糕机都写一个代理类(ApricotCakeProxy、ApricotBreadProxy、ApricotCookieProxy等)。可是若是使用动态代理的话,咱们只须要写一个通用的撒杏仁代理类(ApricotHandler)就能够直接完成全部操做了。直接省去了写 ApricotCakeProxy、ApricotBreadProxy、ApricotCookieProxy 的功夫,极大地提升了效率。
看到这里,你们应该清楚为何有了静态代理以后,还须要有动态代理了吧。静态代理只能针对某一接口(面包 或 蛋糕)进行操做,若是要对全部接口都(全部产品)都能作同样操做,那就必需要动态代理出马了。
咱们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:
public interface Waiter { public void serve(); }
public class ManWaiter implements Waiter { public void serve() { System.out.println("服务..."); } }
如今咱们要对ManWaiter对象进行加强,建立代理对象,要让它在服务以前以及服务以后添加礼貌用语,即在服务以前说“您好!”,在服务以后说:“很高兴为您服务!”。
public class WaiterInvocationHandler implements InvocationHandler { // 目标对象 private Waiter waiter; public WaiterInvocationHandler(Waiter waiter) { this.waiter = waiter; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("您好!"); // 调用目标对象的目标方法 Object result = method.invoke(waiter, args); System.out.println("很高兴为您服务!"); return result; } }
写完代理对象后,测试:
public class WaiterDemo { public static void main(String[] args) { // 目标对象 Waiter manWaiter = new ManWaiter(); // 参数manWaiter表示目标对象 WaiterInvocationHandler waiterInvocationHandler = new WaiterInvocationHandler(manWaiter); // 获得代理对象,代理对象就是在目标对象的基础上进行了加强的对象! Waiter waiterProxy = (Waiter) Proxy.newProxyInstance(manWaiter.getClass().getClassLoader(), manWaiter.getClass().getInterfaces(), waiterInvocationHandler); // 前面添加"您好",后面添加"很高兴为您服务" waiterProxy.service(); } }
输出结果为:
上面的两个例子中使用的就是JDK动态代理方式,咱们能够知道要实现JDK动态代理须要作两方面的工做。
//杏仁动态代理 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) { //给蛋糕加上杏仁 FruitCakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler apricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine cakeMachine = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), apricotHandler); cakeMachine.makeCake(); }
【Proxy.newProxyInstance】
Object proxyObject = Proxy.newProxyInstance(ClassLoader classLoader, Class[] interfaces, InvocationHandler h);
动态代理其实指的是一种设计模式概念,指的是经过代理来作一些通用的事情,常见的应用有权限系统、日志系统等,都用到了动态代理。
而 Java 动态代理只是动态代理的一种实现方式而已,动态代理还有另一种实现方式,即 CGLib(Code Generation Library)。
Java 动态代理只能针对实现了接口的类进行拓展,因此细心的朋友会发现咱们的代码里有一个叫CakeMachine的接口。而 CGLib 则没有这个限制,由于 CGLib 是使用继承原有类的方式来实现代理的。
咱们仍是举个例子来讲明 CGLib 是如何实现动态代理的吧。仍是前面的例子:咱们要作杏仁水果蛋糕、巧克力水果蛋糕、五仁巧克力蛋糕,这时候用代码描述是这样的。
首先咱们须要写一个杏仁拦截器类,这个拦截器能够给作好的蛋糕加上杏仁。(注意:Java工程中使用cglib须要导入cglib-nodep-2.1_3.jar)
public class ApricotInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { methodProxy.invokeSuper(o, objects); System.out.println("adding apricot..."); return o; } }
接着直接让蛋糕店使用 CGLib 提供的工具类作杏仁水果蛋糕:
public class CakeShop { public static void main(String[] args) { //CGLib动态代理(能够同时给蛋糕、面包等加杏仁) Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(FruitCakeMachine.class); enhancer.setCallback(new ApricotInterceptor()); FruitCakeMachine fruitCakeMachine = (FruitCakeMachine) enhancer.create(); fruitCakeMachine.makeCake(); } }
上面的 enhancer.setSuperClass() 设置须要加强的类,而 enhancer.setCallback() 则设置须要回调的拦截器,即实现了 MethodInterceptor 接口的类。最后最后使用 enhancer.create() 生成了对应的加强类,最后输出结果为:
和咱们预期的同样。若是要作一个杏仁面包片,那么直接让蛋糕店利用ApricotHandler 再作一个就能够了,它们的区别只是传入的加强类不一样。
public class CakeShop { public static void main(String[] args) { //作一个杏仁面包片 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RedBeanBreadMachine.class); enhancer.setCallback(new ApricotInterceptor()); RedBeanBreadMachine chocolateCakeMachine = (RedBeanBreadMachine) enhancer.create(); chocolateCakeMachine.makeBread(); } }
能够看到,这里传入的加强类是 RedBeanBreadMachine,而不是以前的 FruitCakeMachine。
对比 Java 动态代理和 CGLib 动态代理两种实现方式,你会发现 Java 动态代理适合于那些有接口抽象的类代理,而 CGLib 则适合那些没有接口抽象的类代理。
从上面的例子咱们能够知道,Java 动态代理的入口是从 Proxy.newInstance() 方法中开始的,那么咱们就从这个方法开始边剖析源码边理解其原理。
其实经过这个方法,Java 替咱们生成了一个继承了指定接口(CakeMachine)的代理类(ApricotHandler)实例。从 Proxy.newInstance() 的源码咱们能够看到首先调用了 getProxyClass0 方法,该方法返回了一个 Class 实例对象,该实例对象其实就是 ApricotHandler 的 Class 对象。接着获取其构造方法对象,最后生成该 Class 对象的实例。其实这里最主要的是 getProxyClass0() 方法,这里面动态生成了 ApricotHandler 的 Class 对象。下面咱们就深刻到 getProxyClass0() 方法中去了解这里面作了什么操做。
getProxyClass0() 方法首先是作了一些参数校验,以后从 proxyClassCache 参数中取出 Class 对象。其实 proxyClassCache 是一个 Map 对象,缓存了全部动态建立的 Class 对象。从源码中的注释能够知道,若是从 Map 中取出的对象为空,那么其会调用 ProxyClassFactory 生成对应的 Class 对象。
在 ProxyClassFactory 类的源码中,最终是调用了 ProxyGenerator.genrateProxyClass() 方法生成了对应的 class 字节码文件。
到这里,咱们已经把动态代理的 Java 源代码都解析完了,如今思路就很清晰了。
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法简单来讲执行了如下操做:
一、生成一个实现了参数 interfaces 里全部接口且继承了 Proxy 的代理类的字节码,而后用参数里的 classLoader 加载这个代理类。
二、使用代理类父类的构造函数 Proxy(InvocationHandler h) 来创造一个代理类的实例,将咱们自定义的 InvocationHandler 的子类传入。
三、返回这个代理类实例,由于咱们构造的代理类实现了 interfaces(也就是咱们程序中传入的 fruitCakeMachine.getClass().getInterfaces() 里的全部接口,所以返回的代理类能够强转成 MachineCake 类型来调用接口中定义的方法。
由于 JVM 并不容许在运行时修改原有类,因此全部的动态性都是经过新建类来实现的,上面说到的 Java 动态代理也不例外。因此对于 CGLib 动态代理的原理,其实也是经过动态生成代理类,最后由代理类来完成操做实现的。
对于 CGLib 动态代理的实现,我并无深刻到源码中,而是经过查阅资料了解了其大概的实现原理。
而在 CGLib 生成新类的过程当中,其使用的是一个名为 ASM 的东西,它对 Java 的 class 文件进行操做、生成新的 class 文件。若是你对 CGLib 的原理感兴趣,不妨看看这篇文章:从兄弟到父子:动态代理在民间是怎么玩的?
动态代理在代码界但是有很是重要的意义,咱们开发用到的许多框架都使用到了这个概念。我所知道的就有:Spring AOP、Hibernate、Struts 使用到了动态代理。