窗外阴绵绵的,一场疾走的雨在上午短暂地到来又消失。天气有些闷,一个人在卧室无聊时敲几段代码,到是能有一丝心安。
因为疫情影响,至今还未开学,在家自学spring框架,期间遇到了很多问题。
讲动态代理之前,先了解为什么需要动态代理。动态代理能解决相当多的问题,但是按照其代理的进化关系,应该说是为了弥补静态代理的不足,在静态代理的思想上增强其实力。
增强什么实力?就要了解静态代理有什么不足。但是我们得先知道静态代理已经具备怎样的实力。
我最直观的理解,静态代理的目的就是为了对一个对象的行为能力进行增强。先举一个静态代理的例子。
例子:一个行人要出行,他需要先实现拥有出行这个行为的接口,下面是接口代码。
package静态代理;publicinterfaceTrip{//pedestrian:行人,trip:出行voidgout();}既然是行人出行,就要有人实现出行这个行为,下面是接口的行人实现。
package静态代理;publicclassPedestrianimplementsTrip{//pedestrian:行人@Overridepublicvoidgout(){//TODOAuto-generatedmethodstubSystem.out.println("行人出行……");}}行人出行有许多方式,可以乘船,坐火车汽车或者飞机等,为了方便,这里让行人乘坐汽车出行,这样就需要让行人坐进汽车内让汽车跑到终点,相当于对行人出行行为的增强,如下代码。
package静态代理;publicclassCarimplementsTrip{privateTriptrip;publicCar(Triptrip){this.trip=trip;}@Overridepublicvoidgout(){//TODOAuto-generatedmethodstubbeforeGout();//出行前增强处理trip.gout();afterGout();//出行后增强处理}publicvoidbeforeGout(){System.out.println("出行方式:乘坐汽车出行");}publicvoidafterGout(){System.out.println("即将到达目的地,请乘客注意准备下车!");}}最后来看客户端代码:
packagetest;import静态代理.Car;import静态代理.Pedestrian;import静态代理.Trip;publicclassTest{publicstaticvoidmain(String[]args){//第一步,一个人要出行,实例化出一个有出行行为的行人。Trippedestrian=newPedestrian();//第二步,对行人的出行方式进行增强,让行人乘坐汽车出行。Tripcar=newCar(pedestrian);//第三步,行人完成出行行为。car.gout();}}运行结果为:
出行方式:乘坐汽车出行行人出行……即将到达目的地,请乘客注意准备下车!
相同的例子,当没有小汽车为其增强出行方式的时候,其运行结果为:
行人出行……
通过对比,我们可以发现,不仅行人出行的动作由汽车完成了,而且汽车还为这个动作做出了详细的情况描述。
这就是静态代理。通过代理方式,让行人自己不想做的事情由来汽车代理来完成,并且提供了增强服务。
静态代理总结
优点:
公共的业务由代理来完成,实现了业务的分工;
公共业务的扩展变得更加集中和方便。
缺点:
类爆炸,也就是当有很多除了行人以外的类需要代理时,就需要重新写很多个代理来满足。这样一个项目就会有大量的类。(感觉大部分教程都并没讲清楚为什么会类爆炸,其实如果只有行人类,出行接口,汽车类,那么再来一个快递类,只要实现了出行接口,同样可以让快递装入汽车并通过实例化的汽车类执行以上操作,而并未需要编写额外的汽车代理类。那么到底为什么会产生类爆炸呢,因为对动态代理理解不够,此处先埋下伏笔。)
特点:
编译时代理对象便产生了。
为了解决静态代理的问题,动态代理应运而生。
动态代理主要有两种实现方式。
第一种,基于JDK,需要实现代理的代理委托类必须存在接口。
第二种,基于cglib,需要实现代理的代理委托类必须被继承。
这里先来第一种动态代理的解决方案:
预备知识:
T.class.getMethod(StringmethodName,ClassmethodType.class...).invoke(Objectobject,Object[]args)
t.getClass().getMethod(StringmethodName,ClassmethodType.class...).invoke(Objectobject,Object[]args)
t.methodName(...);
其中:Tt=newT();
上面两种种执行invoke()方法的结果相同,其中object就是T类的实例化对象,其中的args就是参数的对象数组。
动态代理主要依据Proxy类和InvocationHandler接口实现(都来自reflect反射包里)。
下面开始用动态代理的方式来实现上面的例子:
一个行人要出行,他需要先实现拥有出行这个行为的接口,下面是接口代码:
package动态代理;publicinterfaceTrip{voidgout();}既然是行人出行,就要有人实现出行这个行为,下面是接口的行人实现。
package动态代理;publicclassPedestrianimplementsTrip{@Overridepublicvoidgout(){//TODOAuto-generatedmethodstubSystem.out.println("行人出行……");}}与静态代理的不同从这里开始了:
行人出行有许多方式,可以乘船,坐火车汽车或者飞机等,为了方便,这里让行人乘坐汽车出行,这样就需要让行人坐进汽车内让汽车跑到终点,相当于对行人出行行为的增强。
但是,不同于静态代理处理问题的方式,为了统一处理任意的类(包括但不限于行人类),对任意的类的行为的增强,就需要让原本代替行人进行跑到的车(对委托方的类进行增强处理的类)继承一个统一的接口InvocationHandler。(在静态代理中这里的Car类原本继承的接口应该是行人实现的接口,也就是Trip)。
如下代码:
package动态代理;importjava.lang.reflect.Proxy;publicclassTest{publicstaticvoidmain(String[]args){//TODOAuto-generatedmethodstub//第一步,首先主角出场,也就是实例化代理委托方Pedestrianpedestrian=newPedestrian();//第二步,主角的奔驰出场,也就是实例化代理方。Carcar=newCar(pedestrian);//第三步,主角拿到了奔驰的车钥匙,也就是代理委托方与代理方捆绑到了一起。Tripp=(Trip)Proxy.newProxyInstance(pedestrian.getClass().getClassLoader(),pedestrian.getClass().getInterfaces(),car);//骑上我心爱的小摩托,它永远也不堵车p.gout();}}运行结果:
至此,动态代理部分算是讲完了。(小朋友你是否有很多问号?)
额,龙岭迷窟更新了,去追更了。基于cglib的动态代理有缘再学,ヾ( ̄▽ ̄)Bye~Bye~。
补充:
动态代理是对静态代理的扩展与增强,动态代理拥有静态代理上述的全部优点,与静态代理不同的是,动态代理是在运行过程中产生代理类,而静态代理是在编译时已经产生。从方法上看,动态代理应该是一个可以动态地写代码的工具。意思就是当你需要些一个静态代理时,动态代理会为你写好原本应该由你写的且过程重复的代码,然后再去执行这段代码动态生成代理对象。动态代理其实本质是一个工具,就是动态生成静态代理的工具。