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