Spring 底层如何创建对象:反射机制与实例化策略

Spring 底层如何创建对象:反射机制与实例化策略 Spring 底层如何创建对象反射机制与实例化策略一、核心结论默认情况下Spring 通过反射调用无参构造方法来创建 Bean 实例。但这只是最常见的情况。Spring 的实例化策略比这更丰富具体取决于 Bean 的定义方式和配置。二、Spring 实例化的完整流程Spring 容器在refresh()阶段会触发preInstantiateSingletons()对所有非懒加载的单例 Bean进行实例化。核心入口在AbstractAutowireCapableBeanFactory.createBeanInstance()方法中。简化后的流程AbstractApplicationContext.refresh() └── finishBeanFactoryInitialization() └── preInstantiateSingletons() └── getBean() → doGetBean() → createBean() └── doCreateBean() └── createBeanInstance() ├── 1. 如果使用 Supplier → 调用 Supplier.get() ├── 2. 如果使用工厂方法 → 调用工厂方法 ├── 3. 如果有多个构造方法 → 自动注入构造方法 └── 4. 否则 → 使用无参构造方法 反射三、三种实例化方式3.1 无参构造方法 反射默认方式这是最常见的场景。当bean配置中没有指定构造参数且类有默认无参构造方法时Spring 使用SimpleInstantiationStrategy通过反射调用无参构造方法。// 源码逻辑简化publicObjectinstantiate(RootBeanDefinitionbd,StringbeanName,BeanFactoryowner){Constructor?constructorToUsebd.getResolvedConstructorOrFactoryMethod();if(constructorToUsenull){Class?clazzbd.getBeanClass();// 获取无参构造方法constructorToUseclazz.getDeclaredConstructor();bd.setResolvedConstructorOrFactoryMethod(constructorToUse);}// 通过反射调用构造方法returnBeanUtils.instantiateClass(constructorToUse);}BeanUtils.instantiateClass()内部publicstaticTTinstantiateClass(ConstructorTctor,Object...args){ReflectionUtils.makeAccessible(ctor);// 设置构造方法可访问returnctor.newInstance(args);// 反射调用}这就是最常见的“通过反射调用无参构造方法创建对象”。3.2 有参构造方法 自动注入如果 Bean 类没有无参构造方法但有一个有参构造方法Spring 会自动使用该构造方法并注入参数构造器注入。ComponentpublicclassUserService{privatefinalUserRepositoryuserRepository;// 只有一个有参构造方法Spring 自动注入参数publicUserService(UserRepositoryuserRepository){this.userRepositoryuserRepository;}}此时 Spring 的实例化策略// 源码简化protectedBeanWrapperautowireConstructor(StringbeanName,RootBeanDefinitionmbd,Constructor?[]ctors,Object[]explicitArgs){// 使用 ConstructorResolver 解析并调用构造方法returnnewConstructorResolver(this).autowireConstructor(beanName,mbd,ctors,explicitArgs);}ConstructorResolver会根据构造方法的参数类型从容器中查找匹配的 Bean 并注入。3.3 工厂方法实例化通过Bean方法或 XML 中的factory-method定义 Bean 时Spring 不会直接反射创建对象而是调用工厂方法。ConfigurationpublicclassAppConfig{BeanpublicDataSourcedataSource(){returnnewHikariDataSource();}}此时 Spring 调用AppConfig实例的dataSource()方法获取返回值作为 Bean。源码走的是instantiateUsingFactoryMethod()逻辑是找到工厂 Bean 和对应的方法通过反射调用该方法。!-- XML 中的工厂方法 --beaniddataSourceclasscom.example.DataSourceFactoryfactory-methodcreate/四、CGLIB 增强为什么不能简单看成一个 new对于普通的无参构造方法Spring 确实是通过反射调用的。但 Spring 可能在实例化前对类进行 CGLIB 增强生成代理类再实例化代理类。// 如果 Bean 需要 AOP 代理Spring 会在 Bean 初始化完成后生成代理对象// 但对 Configuration 类本身Spring 会通过 CGLIB 增强ConfigurationpublicclassAppConfig{BeanpublicUserServiceuserService(){// 这里的 userService() 调用会被 CGLIB 拦截// 确保返回的是容器中的单例returnnewUserService();}}因此Configuration类的实例化过程是先通过 CGLIB 生成子类再实例化子类。CGLIB 生成子类时默认调用父类的无参构造方法。五、Bean 实例化后的处理链对象实例化只是第一步之后还有一系列处理1. 实例化createBeanInstance ← 反射调用构造方法 2. 属性填充populateBean ← 依赖注入Autowired、Value 3. 初始化前BeanPostProcessor.postProcessBeforeInitialization 4. 初始化afterPropertiesSet、init-method、PostConstruct 5. 初始化后BeanPostProcessor.postProcessAfterInitialization← AOP 代理在这里生成 6. 注册到单例池这个流程说明new 对象只是起点Spring 在 new 出来的对象上完成依赖注入、增强、回调等一系列操作最终放入容器。六、循环依赖与三级缓存构造器注入与字段注入在循环依赖时的表现不同这与实例化时机有关。注入方式实例化时机循环依赖支持无参构造 字段注入先实例化再注入属性✅ 支持通过三级缓存提前暴露早期引用构造器注入实例化时需要依赖依赖需要先实例化❌ 不支持循环依赖时抛异常Spring 解决循环依赖时会提前暴露ObjectFactory到singletonFactories三级缓存让依赖方先拿到早期引用。这进一步说明“实例化”和“属性注入”是两个分离的阶段。七、几种实例化方式的对比方式触发条件底层实现实例化时机无参构造 反射默认方式SimpleInstantiationStrategyConstructor.newInstance()属性注入前有参构造 注入无无参构造有唯一有参构造ConstructorResolver.autowireConstructor()实例化时注入依赖工厂方法Bean或factory-methodinstantiateUsingFactoryMethod()调用工厂方法SupplierregisterBean传入 SupplierSupplier.get()调用 SupplierCGLIB 代理Configuration类、需要代理的 BeanCGLIB 子类 父类无参构造初始化后八、常见误区误区一Spring 只能通过无参构造方法创建对象错误。构造器注入、工厂方法、Supplier 都是实例化方式无参构造只是默认情况。误区二Spring 创建对象后就立即注入依赖错误。实例化和属性注入是两个阶段。构造器注入的依赖在实例化时提供字段注入的依赖在实例化后填充。误区三反射调用构造方法就等于new不完全相同。Constructor.newInstance()与new关键字的行为非常接近但反射允许访问私有构造方法且在 JIT 优化下性能差距已大幅缩小。九、总结Spring 创建对象的核心机制是默认通过反射调用无参构造方法实例化 Bean再通过属性填充完成依赖注入最后经过一系列后置处理AOP 代理、初始化回调注册到容器。当 Bean 定义方式不同有参构造、工厂方法、Supplier时实例化策略会相应变化但反射始终是底层手段之一。理解这个机制就能解释很多现象为什么循环依赖通过字段注入能解决而构造器注入不行、为什么Configuration类的Bean方法调用能返回单例、为什么私有构造方法在 Spring 中也能被调用。