面试官让手写动态代理实现?——JDK代理和CGLIB代理解析
一、什么是动态代理
动态代理是相对于静态代理产生的概念。
可以简单理解为,静态代理是在代码中编写好代理类,动态代理的代理类是在运行时动态生成的。
使用代理一般是为了在原类中增加一些增强功能,如Spring的AOP就是通过动态代理实现的。
二、Spring中使用动态代理
有JDK代理和CGLIB代理两种实现。
2.1 JDK代理和CGLIB代理区别
JDK
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CJLIB
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
2.2 何时选用
当Bean实现接口时,Spring就会用JDK的动态代理。
当Bean没有实现接口时,Spring使用CGlib是实现。
可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
三、具体实现
3.1 JDK动态代理实现
1) 一些概念
有两个重要的类和接口InvocationHandler(接口)和Proxy(类)
InvocationHandler
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,业务接口的方法最终都是回调业务处理类(具体的Handler)的invoke方法完成调用
proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
method:我们所要调用某个对象真实的方法的Method对象
args:指代代理对象方法传递的参数
Proxy
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
interfaces:一个interface对象数组,表示我们声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
2)实现
定义一个接口:
实现类:
这里Real就是我们需要代理的类。
动态代理代码:
构造方法把要代理的对象传入Handler中。
这里invoke方法很重要,可以看到方法调用是通过我们传递一个Method类型参数,然后调用method.invoke来实现,即通过反射来实现。
生成代理类的样子:
这里add方法是调用了handler的invoke方法,传递三个参数,第一个是代理类本身,第二个是add方法的反射类,最后一个是参数列表。我们是通过InvocationHandler来完成拦截与代理。
JDK Proxy具体使用:
3) 实现概述
定义一个Handler实现InvocationHandler接口,通过构造方法把要代理的对象传到Handler中。Handler中重写invoke方法,调用method.invoke反射实现方法原逻辑
通过Proxy的newProxyInstance方法创建一个代理对象
生成的代理类会注入InvocationHandler对象,注入Method对象,代理类方法执行需要调用handler的invoke方法,可在其中增加拦截逻辑
3.2 CGLIB动态代理实现
没有接口,直接是实现类:
使用了一个与JDK Proxy中Handler类似的类:
代理类的大致样子:
与jdk代理大致相同,只是多了一个MethodProxy。 去上面Interceptor可以看到,调用invoke方法是
而不是
原因是代理类继承了原始类,obj指向的就是代理类对象的实例(JDK代理中指向的是真实对象)。
如果第二种写法就会递归调用代理类的add方法。
因此cglib封装了一个MethodProxy类,其中invokeSuper方法可以调用原始基类的真正方法。
使用:
总结
JDK代理和CGLIB代理实现本质上是很相似的。
都包含一下两点内容:
有一个接口或者基类,定义了一个代理类。
一个方法拦截器,完成方法的拦截和代理,是所有调用链的桥梁。