动态代理
JDK动态代理
动态代理的实现方式:
1 |
|
原理
newProxyInstance方法返回通过动态代理生成的对象
1 |
|
getProxyClass0方法生成一个代理类
1 | private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { |
其中proxyClassCache的定义如下:
1 | private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); |
KeyFactory将接口数组映射为一个key对象:
1 | private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> { |
ProxyClassFactory是一个工程方法,生成、定义、返回给定加载器和接口数组定义的代理类:
1 | private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { |
ProxyGenerator.generateProxyClass生成代理类class字节码
1 | public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) { |
用以下代码可以获取到JDK为我们生成的字节码写到硬盘中:
1 | public class ProxyGeneratorUtils { |
运行以下代码生成代理类文件:
1 | ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy11.class"); |
生成的class文件经过反编译:
1 |
|
CGLIB动态代理
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
假设我们有一个没有实现任何接口的类HelloConcrete:
1 | public class HelloConcrete { |
因为没有实现接口,该类无法使用JDK代理,通过CGLIB代理实现如下:
首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法
1
2
3
4
5
6
7public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("You said: " + Arrays.toString(args));
return methodProxy.invokeSuper(proxy, args);
}
}
在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象
1
2
3
4
5
6Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(new MyMethodInterceptor());
HelloConcrete hello = (HelloConcrete) enhancer.create();
System.out.println(hello.sayHello("I love you!"));
运行上述代理输出结果:
1 | You said: [I love you!] |
原理
我们可以通过将代理类生成class文件,加入以下的代码,可以将代理类保存为指定路径下的文件:
1 | System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/wangqi/Downloads"); |
生成的代理如下所示:
1 | public class HelloConcrete$$EnhancerByCGLIB$$a810359d extends HelloConcrete implements Factory { |
我们看到HelloConcrete$$EnhancerByCGLIB$$a810359d
是CGLIB代理之后的对象类型,父类是HelloConcrete,同时实现了net.sf.cglib.proxy.Factory
接口,这个接口是CGLIB自己加入的,包含一些工具方法。
我们重点来看一下经过代理生成的sayHello
方法:
1 | public final String sayHello(String var1) { |
其中var10000
是我们在代理定义时绑定的MethodInterceptor
——MyMethodInterceptor
。如果这个interceptor不为null,则调用它的intercept
方法;否则调用父类的sayHello
方法。
我们知道final类型不能有子类,所以CGLIB不能代理final类型,遇到这种情况会抛出异常。同样的,final方法也是不能重载的,所以也不能通过CGLIB代理,遇到这种情况不会抛出异常,而是会跳过final方法只代理其他方法,即遇到这个方法直接调用父类的方法。
参考资料:
http://rejoy.iteye.com/blog/1627405
http://paddy-w.iteye.com/blog/841798