接下来让我们把注意力重新回到先前被淡化的错误处理上来。在异常处理1处,由于我们有理由确保所有的信息如接口名、方法名和参数类型都准确无误,所以这部分异常发生的概率基本为零,所以基本可以忽略。而异常处理2处,我们需要思考得更多一些。
回想一下,接口方法可能声明支持一个异常列表,而调用处理器invoke方法又可能抛出与接口方法不支持的异常,再回想一下先前提及的Java动态代理的关于异常处理的特点,对于不支持的异常,必须抛UndeclaredThrowableException运行时异常。所以通过再次推演,我们可以得出一个更加清晰的异常处理2的情况:
Objectr=null;
try{ r=handler.invoke(this, method, newObject[]{newInteger(arg1),newLong(arg2),arg3});
}catch(ExceptionAe){
//接口方法支持ExceptionA,可以抛出
throwe; }catch(ExceptionBe){
//接口方法支持ExceptionB,可以抛出
throwe; }catch(Throwablee){
//其他不支持的异常,一律抛UndeclaredThrowableException
这样我们就完成了对动态代理类的推演实现。推演实现遵循了一个相对固定的模式,可以适用于任意定义的任何接口,而且代码生成所需的信息都是可知的,那么有理由相信即使是机器自动编写的代码也有可能延续这样的风格,至少可以保证这是可行的。
美中不足
诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。
有很多条理由,人们可以否定对class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。