上一篇用了JDK的动态代理,说JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,却有些问题一直没有想明白。比如InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,动态代理的实现步骤等等。
下面先来说说动态代理的实现步骤:
(1) 实现InvocationHandler接口创建自己的调用处理器;
(2) 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类;
(3) 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数;
(4) 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
Java代码:
<span style="font-family:Microsoft YaHei;">// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); </span>
Proxy类的静态方法newProxyInstance对后三步做了封装,简化了动态代理对象的获取过程。简化后的java代码如下:
<span style="font-family:Microsoft YaHei;">// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class }, handler ); </span>
下面我只简单介绍一下在代理对象调用方法并返回结果时用到的invoke
( Object proxy, Methodmethod, Object[] args) throws Throwable方法的参数。
proxy | 代理对象; |
method | 要调用的 Method实例。Method对象的声明是在该代理对象实现的接口中定义了的。 |
args | 包含传入代理实例上方法调用的参数值数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如java.lang.Integer或java.lang.Boolean)的实例中。 |
return | 从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为null并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出ClassCastException。 |
Throws | Throwable:从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的throws子句中声明的任一异常类型或未经检查的异常类型java.lang.RuntimeException或java.lang.Error。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的throws子句中声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的UndeclaredThrowableException。 |
对于是由谁来调用InvocationHandler的invoke方法,可以参考一下这个博客链接:http://rejoy.iteye.com/blog/1627405,讲解的很详细。从讲解中可以看出,其实动态代理的核心其实就是代理对象的生成,即Proxy.newProxyInstance(classLoader, proxyInterface, handler)。
动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题。
第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。
第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,经过测试大概每个代理类比静态代理多出10几毫秒的消耗。其次使用反射大量生成类文件可能引起Full GC造成性能影响。因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC。所以当你大量使用动态代理时,可以将持久化设置大一些,减少Full GC次数。
而如果采用CgLib 实现动态代理,它一方面不必非要实现一个接口,另一方面性能也比JDK动态代理性能好。下篇博客我们就讲一下CgLib 实现动态代理,敬请期待吧!