概述
一个工作5年的Java程序员,去应聘得物APP月薪30k的Java开发岗位。
被一个基础问题问得措手不及这个问题是:JDK动态代理为什么只能代理有接口的类。
这个问题在他的认知里面,是一个很正常的事情,竟然还有问什么?
问题解答
这个问题的核心本质,是JDK动态代理本身的机制来决定的(如图)。
首先,在Java里面,动态代理是通过Proxy.newProxyInstance()方法来实现的,它需要传入被动态代理的接口类。
编辑
之所以要传入接口,不能传入类,还是取决于JDK动态代理的底层实现(如图)。JDK动态代理会在程序运行期间动态生成一个代理类$Proxy0,这个动态生成的代理类会继承java.lang.reflect.Proxy类,同时还会实现被代理类的接口IHelloService。
在Java中,是不支持多重继承的。而每个动态代理类都会继承Proxy类(这也是JDK动态代理的实现规范),所以就导致JDK里面的动态代理只能代理接口,而不能代理实现类。
编辑
分析过动态代理的源码,发现Proxy这个类只是保存了动态代理的处理器InvocationHandler,如果不抽出来,直接设置到$Proxy0动态代理类里面,也是可以的。如果这么做,就可以针对实现类来做动态代理了。作者为什么这么设计,认为有几个方面的原因。
-
动态代理本身的使用场景或者需求,只是对原始实现的一个拦截,然后去做一些功能的增强或者扩展。而实际的开发模式也都是基于面向接口来开发,所以基于接口来实现动态代理,从需求和场景都是吻合的。当然确实可能存在有些类没有实现接口的,那这个时候,JDK动态代理确实无法满足。
-
在Java里面,类的继承关系的设计,更多的是考虑到共性能力的抽象,从而提高代码的重用性和扩展性,而动态代理也是在做这样一个事情,它封装了动态代理类生成的抽象逻辑、判断一个类是否是动态代理类、InvocationHandler的持有等等,那么把这些抽象的公共逻辑放在Proxy这个父类里面,很显然是一个比较正常的设计思路。
总的来说,认为这个设计上并没有什么特别值得讨论的地方,因为认为技术方案的设计是解决特定场景问题的。
如果一定要针对普通类来做动态代理,可以选择cglib这个组件,它会动态生成一个被代理类的子类,子类重写了父类中所有非final修饰的方法,在子类中拦截父类的所有方法调用从而实现动态代理。
结尾
开发、架构的程序员,越往上走,对于计算机基础以及底层知识的理解越来越重要。上层的应用框架可能会随着业务的发展发生变化,但是底层的技术原理和设计理念是不会变化的。
所以,掌握了基础,就能够以不变应对万变。
从另外一个角度来说,大家都停留在业务框架的使用上,短期能带来较大的收益,但是随着生产工具的升级,比如人工智能、低代码、零代码、saas、paas逐步成熟落地,那业务程序员的价值会被逐步弱化。
这就像曾经的工业革命,工业革命之前手工劳作不管效率多高,技巧多么熟练,一旦由机器来取代,那传统的手工劳动力必然会被淘汰,伴随而来的就是技能的升级-从传统
手工劳作转向了面向机器的操作。
所以,底层逻辑还是对一个事物本质的认识,而不是停留在表面!