Spring 的反射反射机制,说白了就是机制让你在运行时才决定“用哪个类、调哪个方法、反射改哪个字段”。机制编译期你写的反射是
Class<?>、Method、机制Field这些抽象概念,反射真正跑起来才把具体类拽出来干活。机制没有它,反射Spring 的机制依赖注入、AOP、反射注解解析基本都转不动。机制我最早对反射有感觉,反射是机制看 Spring 怎么把
@Autowired塞进一个私有字段。字段是反射 private 的,外面根本碰不到,但 Spring 拿到Field对象后直接setAccessible(true),然后field.set(bean, dependency),就这么硬塞进去了。当时觉得有点“暴力”,可它确实管用。你写业务代码时不会这么干,但框架层需要这种绕过编译期检查的能力。Spring 里反射出现最密集的地方,是 bean 的实例化阶段。配置文件或注解里只给了类名,比如
com.example.OrderService,Spring 得先Class.forName()拿到Class对象,再找构造器。默认走无参构造,clazz.getDeclaredConstructor().newInstance()就创建出实例。如果构造器有参数,比如构造器注入,Spring 会先解析参数类型,再递归去容器里找匹配的 bean,最后constructor.newInstance(args)。整个过程编译期完全不知道 OrderService 长什么样,全靠运行时拼出来。方法调用也一样。AOP 代理里,
@Before、@Around这些通知最终要落到目标方法上。Spring 拿到Method对象后,method.invoke(target, args)就完事了。但这里有个细节:如果目标方法是 private 的,invoke 会抛IllegalAccessException,得先setAccessible(true)。JDK 17 之后模块化限制更严,有些包默认不开放反射,Spring 6 为此改了不少内部实现,比如用MethodHandles替代部分反射调用,性能更好,权限检查也更细。注解解析更是离不开反射。你写
@Transactional,Spring 得知道这个注解贴在哪个类、哪个方法上。它通过clazz.getAnnotations()或method.getAnnotation(Transactional.class)拿到注解实例,再读里面的属性,比如propagation、isolation。没有反射,注解就只是源码里的标记,运行时毫无意义。Spring 的AnnotationUtils还处理了元注解、组合注解这些复杂情况,底层还是getDeclaredAnnotations()那一套。不过反射不是没代价。每次
getMethod、invoke都有安全检查和参数装箱,比直接调用慢不少。Spring 做了缓存,ReflectionUtils里把Method、Field对象存进ConcurrentHashMap,避免重复查找。但invoke本身的开销躲不掉,所以 Spring 在关键路径上会生成字节码,比如 CGLIB 代理直接继承目标类,方法调用走的是子类重写,不是反射。反射更多用在启动阶段和低频操作上。有个坑我踩过:泛型擦除。反射拿到的
Method参数类型是Object,不是List<String>。Spring 解析泛型依赖时得靠ParameterizedType、ResolvableType这些工具,从Method的getGenericParameterTypes()里挖出真实类型。如果泛型嵌套深了,比如Map<String, List<Order>>,解析代码会变得很绕。这也是为什么有些 Spring 泛型注入的 bug 特别难查。反射让 Spring 有了“看透”字节码的能力,但用多了代码可读性会下降。你读
顶: 4踩: 3228beanFactory.getBean("orderService")很直观,可一旦追到AbstractAutowireCapableBeanFactory里,满屏都是Class、Constructor、Method,得在脑子里模拟一遍运行时才明白。框架开发者习惯了这种思维,业务开发者偶尔调一次反射,往往是为了写通用工具,比如自定义注解处理器、对象拷贝、JSON 序列化。Spring 把反射封装得足够好,我们平时感知不到,但它一直在那儿,撑着整个容器运转。






评论专区