Java反射与动态代理,面试必问底层原理一次讲透
2026/9/14 23:25:09 网站建设 项目流程

面试Java高级岗,反射和动态代理几乎是必考题。很多人能背出API,但一问底层就卡壳。其实这两块是Spring、MyBatis等框架的基石,理解透了,很多框架源码自然就通了。

一、反射:运行时的类信息探针

反射的核心是Class对象。类加载后,JVM在方法区生成一个描述该类的运行时数据结构,Class就是访问这个结构的入口。获取Class有三种方式:类名.class对象.getClass()Class.forName("全限定名")。第三种最常用,因为可以在运行时动态加载。

拿到Class后,就能获取MethodFieldConstructor。调用method.invoke(obj, args)时,底层并不是直接执行方法。JVM会先做方法查找、权限检查,再把参数包装成Object[]。真正的调用委托给MethodAccessor:首次调用走Native实现,之后JVM会动态生成一个字节码类(如GeneratedMethodAccessor1),直接调用目标方法。所以反射慢,主要慢在查找、安全检查、参数装箱和无法内联。setAccessible(true)可以关闭安全检查,性能提升明显。

反射是Spring IOC的根基。BeanDefinition里的类名,就是通过Class.forName加载,再反射实例化、注入依赖。MyBatis的Mapper接口没有实现类,也是靠JDK动态代理生成代理对象,把方法调用转成SQL执行。

二、动态代理:运行期生成代理类

动态代理解决的是“不修改源码,增强方法”的问题。Java有两种实现。

JDK动态代理基于接口。核心是Proxy.newProxyInstanceInvocationHandler。调用时,ProxyGenerator会在内存生成一个$Proxy0类,它继承Proxy并实现目标接口。所有接口方法都转发到InvocationHandler.invoke,在invoke里通过反射调用目标方法并插入增强逻辑。为什么必须基于接口?因为生成的代理类已经继承了Proxy,Java单继承,没法再继承目标类。

CGLIB基于继承。它用ASM字节码框架生成目标类的子类,方法拦截走MethodInterceptor。CGLIB的FastClass机制为每个方法建立索引,调用时直接按索引定位,避免了反射,性能更高。但无法代理final类和方法。

Spring AOP的选择策略:目标类有接口默认用JDK,没有则用CGLIB。Spring Boot 2.x后默认全部用CGLIB,因为性能更好且能代理无接口类。

三、面试高频追问

反射为什么慢?方法查找、访问检查、参数装箱、JIT无法内联。优化手段:缓存ClassMethod对象,setAccessible(true),或者用MethodHandleLambdaMetafactory替代。

JDK代理和CGLIB区别?JDK基于接口,生成类实现接口;CGLIB基于继承,生成子类。JDK反射调用,CGLIB用FastClass索引调用。CGLIB不能代理final。

动态代理底层字节码?JDK用ProxyGenerator生成字节码,CGLIB用ASM。生成的代理类在运行期存在于内存,可以通过-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true导出查看。

总结

反射是“看透类”,动态代理是“伪装类”。反射让框架能动态创建对象、调用方法;动态代理让框架能在不侵入业务的前提下增强逻辑。面试时能讲清MethodAccessor的委托机制、$Proxy0的生成结构、FastClass的索引原理,基本就能和其他候选人拉开差距。底层原理并不神秘,拆开看,都是类加载、字节码和反射调用的组合。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询