一.先说说为什么要用动态代理呢?
譬如现在有一个需求:
有个用户服务模块,某些功能比较慢,但是不知道具体是哪一个,现在需要你排查一下每个业务方法执行的耗时情况,以此作为基本依据进一步分析和调优,最简单的思路是在业务层的每一个方法执行之前记录一下时间,执行完毕记录一下时间,再算一下差值就得出每个方法的执行耗时。但是如果直接在每个业务方法添加这样一段计算方法执行耗时的代码:
long startTime = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
System.out.println("执行"+method.getName()+"方法耗时:"+(endTime-startTime)/1000+"s");
添加之后如下:
最直观的缺点就是:
每个业务方法中都做了代码侵入式修改,计算执行时间不应该是业务方法本身的功能
每个业务方法都需要加一段这样的代码,非常冗余,一旦以后需要改动,必须要处处修改
如果以后扩展一个业务方法,那还要在新增的业务方法中也加上这样一段代码,非常不方便
所以综合以上我们考虑的方式就是代理模式,即让代理对象去把我不想做的事情或不该我做的事给做了,我只关注我本身的业务。由于静态代理模式通用性很差,所以本文直接讨论使用动态代理的方式来实现本需求。
二.动态代理的两种实现方式
方式一:jdk本身提供的jdk动态代理,这种方式不需要额外引入依赖,但使用本方式的前提是被代理类必须实现接口
方式二:cglib动态代理,cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,既然是创建被代理类的子类,那被代理类肯定是不能被final修饰的
两者的区别:通过jdk动态代理创建出来的代理对象其实和被代理类是平级的,即都是同一接口的实现,cglib创建的代理类实际上是被代理类的子类,存在继承关系
三.两种代理方式的具体使用:
下面着重讲一下如何在本案例中实现:
总共需要的几个类如下:
在ProxyTest中测试,ProxyUtil负责创建代理对象,UserService是模拟的业务接口,UserServiceImpl是业务实现类
首先我们需要定义一个接口吧,因为你要用jdk动态代理就必须定义被代理类需要实现的接口
接口的实现类,也就是被代理类
生成代理的工具类
我们在这个类中创建了两个不同的工具方法,分别是基于jdk代理和cglib代理来创建代理类,在具体的拦截方法中可以看到我们把被代理对象真正执行的方法前后都做了时间记录,最后计算出方法耗时,这样就完成了代理。注意:cglib动态代理需要引入对应的pom依赖才可以使用!
jdk:
cglib:
最后在测试类中分别调用两种方式生成的代理对象来执行业务方法
查看执行结果:
可以看到每个业务方法都被代理对象做了方法增强,实现了本案例的需求,同时不需要对原本的业务代码做任何侵入式的修改,即便之后在该业务类中增加了方法,那么该方法一出生便拥有了方法增强,这就是动态代理的魅力!