设计模式篇的第一个模式,我选整理动态代理模式的相关内容。由于动态代理模式如今常常接触到,是java程序员成长中必需要掌握的一个模式。像开源技术Spring,其赖以成名的AOP(面向切面编程)的背后原理就是动态代理模式。AOP应用的一个典型实例就是日志,咱们都知道,在系统运行过程当中若是系统出了问题,第一排查手段就是查看系统日志。那么系统是怎么记录日志的?用过log4j的都知道如何显式的去打印日志,可还有更多的日志是系统来帮咱们打印出来的,它到底是怎么完成的?是在须要打印日志的地方像咱们本身编程同样去显式调用日志打印方法吗?还有有其余更好的办法?答案看上去更像是后者,由于咱们更相信这些开源技术的设计者(大牛)们在兼容性、扩展性等方面比咱们考虑的更深刻。java
进入正题,讨论什么是动态代理模式。代理咱们都清楚,常常听到“代理人”一说,其实明星的经纪人也是代理人的一种,好比某影视公司找明星拍一部电影,一般都是先找到他的经纪人进行初步协商,谈好后再由明星本人出面完成实际电影拍摄,经纪人代理了拍摄前的一些事物,但拍摄过程仍是由被代理人(明星)本身来完成。下面用程序来模拟一下上面的内容:git
首先建立一个接口,里面含有一个方法:程序员
/** * 影视拍摄 */ public interface ShootingWork { /** * 拍摄影视 */ void shooting(); }
而后建立一个电影明星,电影明星能够拍摄电影,所以由它实现上面的接口:github
/** * 电影明星 */ public class FilmStar implements ShootingWork{ @Override public void shooting() { System.out.println("电影明星准备拍摄"); System.out.println("电影明星拍摄中"); System.out.println("电影明星拍摄完成"); } }
而后在实现一个代理人,它负责对外代理电影明星的电影拍摄事务:编程
** * 经纪人 */ public class FilmProxy implements ShootingWork { private ShootingWork star = new FilmStar(); @Override public void shooting() { /** * 处理一些事务后,调用实际执行人的方法 */ System.out.println("经纪人协商合同"); System.out.println("签合同。。。"); this.star.shooting(); } }
最后咱们来完成整个过程的模拟,建立一个客户端:设计模式
/** * 客户端 */ public class ProxyClient { @Test public void filming(){ ShootingWork film = new FilmProxy(); film.shooting(); } }
控制台输出结果:bash
经纪人协商合同 签合同。。。 电影明星准备拍摄 电影明星拍摄中 电影明星拍摄完成 Process finished with exit code 0
能够看到,经过简单的几步,一个代理的模式就实现了,其实这是一个典型的静态代理。咱们分析上面模式,发现一个代理只能对于一个实际委托人(明星),并且代码中必须明确指定代理的委托人。也就是编码中就必须肯定代理人、委托人的关系。而实际生活中的代理远比这个模式复杂。咱们再来举一个NBA的例子,NBA球员跟球队协商合同时,每每也是经过经纪人(经纪公司)的方式,并且一个经纪人每每明星会签约(代理)多个NBA球员。 这样,当球队找到经纪人时,事前(假想)并不知道这个球队要签约我名下的哪名球员,只有进一步沟通后才能肯定。咱们把这种方式称之为动态代理(如理解有误,请指出)。咱们仍是以明星这个为例编写demo,只不过如今一个经纪人代理多个明星,影视公司找人拍摄影视时,并不知道是找哪位,具体详谈后才能肯定下来。按照这个思路咱们来看JDK中动态代理如何实现:ide
首先实现JDK的InvocationHandler接口,做为动态代理人:this
/** * 实现调用类 */ public class ShootInvocationHandler implements InvocationHandler { private ShootingWork star; /** * 构造方法中动态传入对象 * * @param star */ public ShootInvocationHandler(ShootingWork star) { this.star = star; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("动态代理人开始协商合同"); System.out.println("动态代理人开始签合同"); return method.invoke(star, args); } }
建立个简单的工厂类(工厂模式后面再补)实现实例的获取:编码
/** * 工厂类 */ public class StarFactory { public static ShootingWork getStarInstance(String type) { if ("film".equals(type)) { return new FilmStar(); } return null; } }
最后咱们看客户端执行时的变化:
/** * 客户端 */ public class ProxyClient { /** * 静态代理 */ @Test public void filming() { ShootingWork film = new FilmProxy(); film.shooting(); } /** * 动态代理 */ @Test public void dyProxy() { ShootingWork film = StarFactory.getStarInstance("film"); ShootingWork subject = (ShootingWork) Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { ShootingWork.class }, new ShootInvocationHandler(film)); subject.shooting(); } }
执行结果:
动态代理人开始协商合同 动态代理人开始签合同 电影明星准备拍摄 电影明星拍摄中 电影明星拍摄完成 Process finished with exit code 0
这样一个动态代理就实现了。咱们试着分析下好处在哪,当再代理一个电视明星、歌手、曲艺明星时,只须要实现ShootWork的接口,而后修改工厂里面的方法便可,而不须要去从新建立代理类了。这是动态代理JDK自带的实现方式。
还有一种实现动态代理的方式,那就是cglib。从上面jdk原生的动态代理实现方式能够看出,使用时委托对象须要实现一个或多个接口才能实现动态代理的机制,这也算是这种方式的一个缺点吧。若是想被代理的对象(委托对象)不用去实现任何接口,就能够被动态代理怎么办?答案就是cglib的动态代理方式。咱们来看如何使用cglib来实现一个动态代理:
首先定义一个委托对象:
/** * 一名演员 */ public class ActualActor { public String doSomething(String something) { return "ActualActor is doing " + something; } }
实现cglib提供的MethodInterceptor接口,这里只作简单实现便可:
/** * CGlib提供的接口实现 */ public class DoMethodIterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); } }
最后看客户端如何实现动态代理调用:
public class CglibClient { @Test public void testCglib() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ActualActor.class); enhancer.setCallback(new DoMethodIterceptor()); ActualActor actualActor = (ActualActor) enhancer.create(); System.out.println(actualActor.doSomething("拍电影")); System.out.println(actualActor.doSomething("拍电视")); } }
咱们看下执行结果:
ActualActor is doing 拍电影 ActualActor is doing 拍电视 Process finished with exit code 0
比较JDK原生的动态代理实现方式,cglib的方式彷佛更简便一些,代码更清晰。在实际工做中,能够根据实际须要去选择实现方式。
源码获取方式:
github地址:https://github.com/walker0819/designpattern