6.0 KiB
一、为什么需要用代理
案例
突然领导有个需求:调用IService接口中的任何方法的时候,需要记录方法的 耗时。此时你会怎么做呢? IService接口有2个实现类ServiceA和ServiceB,我们可以在这两个类的所有方法中加上统计耗时的代码,如果IService接口有几十个实现,是不是要修改很多代码,所有被修改的方法需重新测试?是不是非常痛苦,不过上面这种修改代码的方式倒是可以解决问题,只是增加了很多工作量(编码 & 测试)。 突然有一天,领导又说,要将这些耗时统计发送到监控系统用来做监控报警使用。 此时是不是又要去一个修改上面的代码?又要去测试?此时的系统是难以维护。 还有假如上面这些类都是第三方以jar包的方式提供给我们的,此时这些类都是class文件,此时我们无法去修改源码。 比较好的方式:可以为IService接口创建一个代理类,通过这个代理类来间接访问IService接口的实现类,在这个代理类中去做耗时及发送至监控的代码
二、JDK动态代理
jdk中为实现代理提供了支持,主要用到2个类: java.lang.reflect.Proxy java.lang.reflect.InvocationHandler jdk自带的代理使用上面有个限制,只能为接口创建代理类,如果需要给具体的类创建代理类,需要用cglib
Proxy
1.getProxyClass方法
- loader:定义代理类的类加载器
- interfaces:指定需要实现的接口列表,创建的代理默认会按顺序实现interfaces指定的接口
2.newProxyInstance方法
创建代理类的实例对象
!
这个方法先为指定的接口创建代理类,然后会生成代理类的一个实例,最后一个参数比较特殊,是InvocationHandler类型的
3.isProxy方法
4.getInvocationHandler
InvocationHandler
!
方法会返回一个代理对象,当调用代理对象的任何方法的时候,会就被 InvocationHandler 接口的 invoke 方法处理,所以主要代码需要卸载 invoke 方法中
创建代理
方式一
- 调用Proxy.getProxyClass方法获取代理类的Class对象
- 使用InvocationHandler接口创建代理类的处理器
- 通过代理类和InvocationHandler创建代理对象
- 上面已经创建好代理对象了,接着我们就可以使用代理对象了
!

方式二
案例改造
任意接口中的方法耗时统计,通过jdk动态代理实现一个通用的代理,解决统计所有接口方法耗时的问题。
!
三、cglib代理
什么是cglib
cglib是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口;本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。Enhancer可能是CGLIB中最常用的一个类,和jdk中的Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对final类进行代理操作。
cglib组成结构
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy和BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。 spring已将第三方cglib jar包中所有的类集成到spring自己的jar包中。
基础使用
!
!
上面代码中的注释很详细,列出了给指定的类创建代理的具体步骤,整个过程中主要用到了
Enhancer类和 MethodInterceptor 接口。
enhancer.setSuperclass 用来设置代理类的父类,即需要给哪个类创建代理类,此处是Service1。
enhancer.setCallback 传递的是 MethodInterceptor 接口类型的参数, MethodInterceptor
接口有个 intercept 方法,这个方法会拦截代理对象所有的方法调用。
还有一个重点是 Object result = methodProxy.invokeSuper(o, objects); 可以调用被代理
类,也就是Service1类中的具体的方法,从方法名称的意思可以看出是调用父类,实际对某个类创建代理,cglib底层通过修改字节码的方式为Service1类创建了一个子类。
实现通用的统计任意类方法耗时代理类
四、CGLIB和JAVA动态代理的区别
- java动态代理只能对接口进行代理,不能对普通类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);cglib能够代理普通类
- java动态代理使用Java原生的反射API进行操作,在生成类比较高效;cglib使用asm框架直接对字节码进行操作,在类的执行过程中比较高效















