Java动态代理 深度详解 (3)

Java 动态代理只能针对实现了接口的类进行拓展,所以细心的朋友会发现我们的代码里有一个叫 MachineCake 的接口。而 CGLib 则没有这个限制,因为 CGLib 是使用继承原有类的方式来实现代理的。

我们还是举个例子来说明 CGLib 是如何实现动态代理的吧。还是前面的例子:我们要做杏仁水果蛋糕、巧克力水果蛋糕、五仁巧克力蛋糕,这时候用代码描述是这样的。

首先我们需要写一个杏仁拦截器类,这个拦截器可以给做好的蛋糕加上杏仁。

public class ApricotInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { methodProxy.invokeSuper(o, objects); System.out.println("adding apricot..."); return o; } }

接着直接让蛋糕店使用 CGLib 提供的工具类做杏仁水果蛋糕:

public class CakeShop { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(FruitCakeMachine.class); enhancer.setCallback(new ApricotInterceptor()); FruitCakeMachine fruitCakeMachine = (FruitCakeMachine) enhancer.create(); fruitCakeMachine.makeCake(); } }

上面的 enhancer.setSuperClass() 设置需要增强的类,而 enhancer.setCallback() 则设置需要回调的拦截器,即实现了 MethodInterceptor 接口的类。最后最后使用 enhancer.create() 生成了对应的增强类,最后输出结果为:

making a Fruit Cake... adding apricot...

和我们预期的一样。如果要做一个杏仁巧克力蛋糕,那么直接让蛋糕店利用ApricotHandler 再做一个就可以了,它们的区别只是传入的增强类不同。

public class CakeShop { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ChocolateCakeMachine.class); enhancer.setCallback(new ApricotInterceptor()); ChocolateCakeMachine chocolateCakeMachine = (ChocolateCakeMachine) enhancer.create(); chocolateCakeMachine.makeCake(); } }

可以看到,这里传入的增强类是 ChocolateCakeMachine,而不是之前的 FruitCakeMachine。

对比 Java 动态代理和 CGLib 动态代理两种实现方式,你会发现 Java 动态代理适合于那些有接口抽象的类代理,而 CGLib 则适合那些没有接口抽象的类代理。

Java动态代理的原理

从上面的例子我们可以知道,Java 动态代理的入口是从 Proxy.newInstance() 方法中开始的,那么我们就从这个方法开始边剖析源码边理解其原理。

Java动态代理 深度详解

其实通过这个方法,Java 替我们生成了一个继承了指定接口(CakeMachine)的代理类(ApricotHandler)实例。从 Proxy.newInstance() 的源码我们可以看到首先调用了 getProxyClass0 方法,该方法返回了一个 Class 实例对象,该实例对象其实就是 ApricotHandler 的 Class 对象。接着获取其构造方法对象,最后生成该 Class 对象的实例。其实这里最主要的是 getProxyClass0() 方法,这里面动态生成了 ApricotHandler 的 Class 对象。下面我们就深入到 getProxyClass0() 方法中去了解这里面做了什么操作。

Java动态代理 深度详解

getProxyClass0() 方法首先是做了一些参数校验,之后从 proxyClassCache 参数中取出 Class 对象。其实 proxyClassCache 是一个 Map 对象,缓存了所有动态创建的 Class 对象。从源码中的注释可以知道,如果从 Map 中取出的对象为空,那么其会调用 ProxyClassFactory 生成对应的 Class 对象。

Java动态代理 深度详解

在 ProxyClassFactory 类的源码中,最终是调用了 ProxyGenerator.genrateProxyClass() 方法生成了对应的 class 字节码文件。

到这里,我们已经把动态代理的 Java 源代码都解析完了,现在思路就很清晰了。Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法简单来说执行了以下操作:

1、生成一个实现了参数 interfaces 里所有接口且继承了 Proxy 的代理类的字节码,然后用参数里的 classLoader 加载这个代理类。

2、使用代理类父类的构造函数 Proxy(InvocationHandler h) 来创造一个代理类的实例,将我们自定义的 InvocationHandler 的子类传入。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssxgw.html