乐于分享
好东西不私藏

深挖Spring IoC启动源码:从初始化到Bean就绪全拆解

深挖Spring IoC启动源码:从初始化到Bean就绪全拆解

核心词:Spring IoC、容器启动、refresh方法、BeanDefinition、Bean初始化、源码解析

Spring IoC容器是Spring框架的“灵魂基石”,我们日常用@Autowired实现Bean自动装配、用@Service标注业务组件,背后都是IoC容器的启动流程在默默支撑。

很多开发者只知其然,不知其所以然——容器是如何初始化的?Bean是如何被扫描、实例化的?依赖注入又是何时完成的?

本文基于Spring 5.3.20源码(Spring Boot 2.7.x对应版本),彻底深挖IoC容器启动全流程,每一步都结合源码片段拆解+实战验证,帮你从根源上搞懂IoC容器的工作逻辑,避开启动常见坑点,真正吃透Spring核心机制。

注:本文聚焦AbstractApplicationContext及其实现类,剥离冗余源码,只保留启动核心链路,兼顾初学者理解和高级开发者深度需求,全程围绕“从初始化到Bean就绪”的核心主线展开。

一、前置铺垫:IoC容器核心组件(必懂)

拆解源码前,先明确3个核心组件,避免后续源码解析脱节,这是理解启动流程的关键:

  1. ApplicationContext:IoC容器顶层接口,继承BeanFactory,不仅负责Bean管理,还提供资源加载、事件发布等扩展功能。

  2. 常用实现类:AnnotationConfigApplicationContext(注解配置)、ClassPathXmlApplicationContext(XML配置)。

  3. AbstractApplicationContext:所有IoC容器的抽象父类,封装了容器启动的核心流程——refresh方法,无论注解还是XML配置,容器启动最终都会委托给这个方法执行。

  4. BeanDefinition:Bean的“元数据描述文件”,记录Bean的类名、scope、依赖关系、初始化方法、销毁方法等核心信息。

关键结论:IoC容器启动的“总入口”是refresh方法,整个流程围绕“解析BeanDefinition→创建Bean→装配依赖→初始化Bean”四大核心动作展开,缺一不可。

补充细节:refresh方法是线程安全的,通过startupShutdownMonitor锁保证同一时间只有一个线程执行容器启动/关闭操作,避免并发创建Bean导致的异常。

二、IoC容器启动全流程拆解(源码级,一步一解析)

整个启动流程分为5个核心阶段,环环相扣,从容器初始化到Bean就绪,每一步都有明确的源码支撑。我们以最常用的AnnotationConfigApplicationContext(注解配置)为例,逐阶段拆解。

阶段1:容器初始化(前置准备,奠定基础)

核心逻辑

容器初始化是启动的第一步,核心任务的是做好前置准备,为后续BeanDefinition解析和Bean实例化铺路,触发时机是创建AnnotationConfigApplicationContext实例时。

具体完成3件事:资源加载器初始化、BeanFactory创建、配置类注册。

源码片段(关键代码,剥离冗余)

// AnnotationConfigApplicationContext构造方法(容器初始化入口)publicAnnotationConfigApplicationContext(Class<?>... componentClasses) {// 1. 调用父类构造方法,初始化资源加载器、BeanFactorythis();// 2. 注册配置类(如@Configuration注解的AppConfig)为BeanDefinition    register(componentClasses);// 3. 核心:启动容器,触发refresh方法    refresh();}// 父类AbstractApplicationContext的构造方法(核心准备)publicAbstractApplicationContext() {// 初始化资源加载器,用于加载类、配置文件等资源this.resourcePatternResolver = getResourcePatternResolver();}// 资源加载器初始化(默认使用PathMatchingResourcePatternResolver)protected ResourcePatternResolver getResourcePatternResolver() {returnnewPathMatchingResourcePatternResolver(this);}

关键细节(逐点拆解)

  • this()方法:AnnotationConfigApplicationContext的无参构造方法,会初始化两个核心工具:   同时调用父类构造方法,初始化默认的BeanFactory——DefaultListableBeanFactory(真正负责Bean创建、依赖注入的“底层工厂”)。

    • AnnotatedBeanDefinitionReader:注解Bean定义读取器,用于解析注解配置。

    • ClassPathBeanDefinitionScanner:类路径Bean扫描器,用于后续扫描Bean。

  • register(componentClasses):将我们定义的配置类(如AppConfig)注册为BeanDefinition:

    • 通过AnnotatedBeanDefinitionReader解析配置类上的注解(@Configuration、@ComponentScan等)。

    • 生成AnnotatedGenericBeanDefinition实例,存入BeanDefinitionMap。

  • 资源加载器:默认使用PathMatchingResourcePatternResolver,支持3种资源路径(classpath:、file:、url:)。

    • 后续扫描Bean时,通过该加载器加载指定包下的.class文件。

    • 关联ApplicationContext的环境(Environment),支持读取配置文件中的占位符。

阶段2:BeanDefinition加载与解析(核心阶段,找Bean)

核心逻辑

这是容器启动最核心的阶段之一,简单说就是容器“找出所有需要管理的Bean,记录其核心信息”。

具体任务:扫描指定包下的Bean → 将其解析为BeanDefinition → 存入BeanFactory的BeanDefinitionMap中。

源码链路(核心流程)

容器启动的核心是refresh方法,其中负责BeanDefinition解析的是invokeBeanFactoryPostProcessors(beanFactory)方法。

核心处理器:ConfigurationClassPostProcessor(Spring内置的BeanFactoryPostProcessor),专门处理注解配置的Bean扫描和解析。

补充细节:BeanFactoryPostProcessor是Spring的扩展接口,作用是在BeanDefinition解析完成后、Bean实例化前,修改BeanDefinition。

// AbstractApplicationContext.refresh()方法(容器启动总入口)@Overridepublicvoidrefresh()throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 前置准备:初始化环境、记录启动时间等        prepareRefresh();// 核心:获取BeanFactory,并执行BeanFactoryPostProcessor(解析BeanDefinition)ConfigurableListableBeanFactorybeanFactory= obtainFreshBeanFactory();// 后续阶段:Bean实例化、初始化等        prepareBeanFactory(beanFactory);try {            finishBeanFactoryInitialization(beanFactory);            finishRefresh();        } catch (BeansException ex) {// 异常处理:销毁已创建Bean,关闭容器            destroyBeans();            cancelRefresh(ex);throw ex;        }    }}// 关键:获取BeanFactory并解析BeanDefinitionprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 刷新BeanFactory,核心是解析BeanDefinition    refreshBeanFactory();return getBeanFactory();}// AnnotationConfigApplicationContext的refreshBeanFactory方法@OverrideprotectedvoidrefreshBeanFactory()throws IllegalStateException {// 初始化BeanFactory(DefaultListableBeanFactory)if (this.beanFactory == null) {this.beanFactory = newDefaultListableBeanFactory(getInternalParentBeanFactory());    }// 执行BeanFactoryPostProcessor,扫描并解析BeanDefinition    invokeBeanFactoryPostProcessors(this.beanFactory);}

解析流程拆解(注解配置为例,4步完成)

  1. 扫描配置类:

    • ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,扫描注册的配置类。

    • 解析配置类上的注解:@ComponentScan(核心)、@Import、@Bean等。

    • @ComponentScan会解析basePackages(扫描包路径)、excludeFilters(排除规则)、includeFilters(包含规则),默认扫描配置类所在包及其子包。

  2. 扫描Bean:

    • ClassPathBeanDefinitionScanner通过资源加载器,获取指定包下的所有.class文件。

    • 排除接口、抽象类、注解类,筛选出带有@Component、@Service、@Repository、@Controller等注解的类。

    • 补充:扫描时会忽略被@Conditional注解排除的类,这是Spring条件装配的核心逻辑。

  3. 解析为BeanDefinition:

    • 将每个扫描到的类,封装为ScannedGenericBeanDefinition实例。

    • 设置核心属性:类名、scope(默认singleton)、lazyInit(默认false)、依赖关系、是否为Primary等。

    • 补充:若类上有@Scope注解,优先设置注解指定的作用域;若为prototype,后续不会在容器启动时实例化。

  4. 注册BeanDefinition:

    • 存入BeanFactory的beanDefinitionMap(ConcurrentHashMap),key为Bean名称(默认类名首字母小写,可通过@Bean的name属性自定义),value为BeanDefinition。

    • 注册前校验Bean名称唯一性,重复注册会抛出BeanDefinitionStoreException。

    • 补充:同时会注册BeanDefinition的别名(若有@AliasFor注解)。

实战验证(查看扫描到的BeanDefinition)

通过代码获取BeanFactory,可直观查看扫描到的BeanDefinition信息,验证解析结果:

// 1. 创建注解配置容器,传入配置类AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(AppConfig.class);// 2. 获取底层BeanFactory(DefaultListableBeanFactory)DefaultListableBeanFactorybeanFactory= (DefaultListableBeanFactory) context.getBeanFactory();// 3. 打印所有BeanDefinition的名称、类名、作用域String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();for (String name : beanDefinitionNames) {BeanDefinitionbeanDefinition= beanFactory.getBeanDefinition(name);    System.out.println("Bean名称:" + name);    System.out.println("Bean类名:" + beanDefinition.getBeanClassName());    System.out.println("Bean作用域:" + beanDefinition.getScope());    System.out.println("------------------------");}// 输出结果(包含配置类、扫描到的业务Bean)// Bean名称:appConfig// Bean类名:com.example.config.AppConfig// Bean作用域:singleton// ------------------------// Bean名称:userService// Bean类名:com.example.service.UserService// Bean作用域:singleton// ------------------------// Bean名称:orderDao// Bean类名:com.example.dao.OrderDao// Bean作用域:singleton

阶段3:Bean实例化(创建Bean对象,核心动作)

核心逻辑

BeanDefinition解析完成后,容器根据BeanDefinition的描述,创建Bean实例,核心是“通过反射创建Bean对象,并完成依赖注入”。

关键规则:      单例Bean:默认在容器启动时实例化。原型Bean:默认在获取时(getBean())实例化。执行顺序:先实例化Bean对象,再注入依赖,避免依赖Bean未创建导致的空指针异常。

源码链路(核心方法)

refresh方法中,负责Bean实例化的核心方法是finishBeanFactoryInitialization(beanFactory),主要完成2件事:

  • 初始化类型转换器(ConversionService),处理Bean属性注入时的类型转换(如String转Integer、String转Date)。

  • 遍历所有BeanDefinition,对非懒加载的单例Bean进行实例化和依赖注入。

补充细节:若未自定义ConversionService,会使用默认的DefaultConversionService。

// 实例化Bean的核心方法protectedvoidfinishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// 1. 初始化类型转换器(用于Bean属性注入时的类型转换,如String转Integer、String转Date)if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {        beanFactory.setConversionService(                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));    } else {// 若未自定义ConversionService,使用默认实现        beanFactory.setConversionService(newDefaultConversionService());    }// 2. 注册BeanPostProcessor(Bean后置处理器,用于后续Bean初始化增强)    beanFactory.addBeanPostProcessor(newApplicationContextAwareProcessor(this));// 3. 核心:实例化所有非懒加载的单例Bean    beanFactory.preInstantiateSingletons();}

实例化流程拆解(核心步骤,5步完成)

Bean实例化的核心逻辑在DefaultListableBeanFactory.preInstantiateSingletons()方法中,具体流程如下:

  1. 筛选Bean:遍历BeanDefinitionMap,筛选出“单例Bean且非懒加载”的Bean。

    • 补充:若Bean被@Lazy注解标注,即使是单例,也不会在容器启动时实例化,而是在第一次getBean()时实例化。
  2. 判断Bean类型:通过BeanDefinition的isFactoryBean()方法,判断是否为FactoryBean(工厂Bean)。

    • 若是FactoryBean:先实例化FactoryBean(存入singletonObjects),再通过FactoryBean的getObject()方法获取目标Bean。

    • 若为普通Bean:直接触发实例化。

    • 补充:FactoryBean可自定义Bean的创建逻辑,Spring内置的有ProxyFactoryBean(AOP代理)、SqlSessionFactoryBean(MyBatis)等。

  3. 调用getBean(beanName),触发Bean实例化,核心分为3步:

    • 注入方式:构造方法注入(createBean阶段完成)、字段注入(@Autowired)、setter注入(populateBean阶段完成)。
    • 补充:createBean方法会先尝试从三级缓存中获取Bean,解决循环依赖,核心逻辑在doCreateBean()方法中。
    • createBean(beanName, mbd, args):通过反射调用Bean的构造方法,创建裸Bean对象(未注入依赖)。

    • populateBean(beanName, mbd, instanceWrapper):依赖注入,将当前Bean依赖的其他Bean,从容器中获取并注入。

    • initializeBean(beanName, exposedObject, mbd):Bean初始化,执行自定义初始化逻辑。

  4. 存入单例缓存:将实例化、注入完成的单例Bean,存入BeanFactory的singletonObjects(单例缓存池),后续获取Bean时直接从缓存中获取,避免重复创建。

  5. 补充:Spring通过三级缓存解决循环依赖,三级缓存分别是:

    • singletonObjects(一级缓存):存储成熟Bean(实例化、注入、初始化完成)。

    • earlySingletonObjects(二级缓存):存储提前暴露的未成熟Bean(实例化完成,未注入依赖)。

    • singletonFactories(三级缓存):存储Bean工厂,用于生成提前暴露的Bean。

关键细节(避坑重点)

  • 构造方法注入优先级最高:

    • 若Bean有多个构造方法,Spring会根据参数类型和数量匹配最合适的。

    • 若有多个匹配的构造方法,会抛出NoUniqueBeanDefinitionException。

    • 若构造方法上有@Autowired注解,会优先选择该构造方法,即使参数不匹配也会尝试注入,失败则抛出异常。

  • 单例缓存池:singletonObjects是ConcurrentHashMap,保证线程安全,这是单例Bean“全局唯一”的核心原因。

    • 补充:单例Bean一旦存入singletonObjects,后续不会再创建,除非容器重启。
  • 原型Bean:

    • 每次getBean()都会创建新的实例,不存入缓存,也不执行容器启动时的实例化。

    • 补充:原型Bean的依赖注入,会在每次实例化时重新获取依赖Bean;若依赖单例Bean则复用,若依赖原型Bean则新建。

  • 遗漏点:若BeanDefinition中指定了factory-method(工厂方法),会通过工厂方法创建Bean,而非直接调用构造方法,这是@Bean注解的核心实现逻辑。

阶段4:Bean初始化(增强扩展,完善Bean)

核心逻辑

Bean实例化和依赖注入完成后,需要进行初始化增强,核心是2件事:

  • 执行自定义的初始化逻辑(如@PostConstruct注解方法)。

  • 触发BeanPostProcessor(Bean后置处理器)的增强方法,实现Spring扩展(如AOP动态代理、事务增强)。

补充细节:区分两个易混淆接口:       BeanPostProcessor:作用于Bean实例化后,处理Bean本身。BeanFactoryPostProcessor:作用于BeanDefinition解析后、Bean实例化前,处理BeanDefinition。

源码片段(初始化核心方法)

protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {// 1. 执行Aware接口回调(如BeanNameAware、ApplicationContextAware)    invokeAwareMethods(beanName, bean);// 2. 执行BeanPostProcessor前置增强(postProcessBeforeInitialization)ObjectwrappedBean= bean;if (mbd == null || !mbd.isSynthetic()) {        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);    }// 3. 执行Bean的自定义初始化方法try {        invokeInitMethods(beanName, wrappedBean, mbd);    } catch (Throwable ex) {thrownewBeanCreationException(mbd.getResourceDescription(), beanName, "初始化方法执行失败", ex);    }// 4. 执行BeanPostProcessor后置增强(postProcessAfterInitialization)if (mbd == null || !mbd.isSynthetic()) {        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);    }return wrappedBean;}

初始化流程拆解(4个核心步骤)

  1. Aware接口回调:

    • 若Bean实现了Aware接口(BeanNameAware、ApplicationContextAware、BeanFactoryAware等),Spring会自动注入对应的资源。

    • 回调顺序:BeanNameAware → BeanClassLoaderAware → BeanFactoryAware → ApplicationContextAware。

    • 补充:Aware接口的实现类不会被BeanPostProcessor增强。

  2. BeanPostProcessor前置增强:

    • 所有注册的BeanPostProcessor,都会执行postProcessBeforeInitialization方法。

    • 作用:对Bean进行前置处理(如初始化前的参数校验、属性修改)。

    • 补充:若该方法返回null,会导致Bean创建失败;返回的对象会作为后续初始化的Bean。

  3. 自定义初始化方法:

    • @PostConstruct:JSR-250规范注解,由CommonAnnotationBeanPostProcessor解析执行。

    • InitializingBean:Spring内置接口,直接回调afterPropertiesSet()方法。

    • init-method:自定义初始化方法,通过反射调用。

    • 三者不可重复执行,按优先级执行一次。

    • 执行优先级:@PostConstruct注解方法 > InitializingBean接口的afterPropertiesSet() > 注解/xml指定的init-method。

    • 细节补充:

  4. BeanPostProcessor后置增强:

    • 所有注册的BeanPostProcessor,都会执行postProcessAfterInitialization方法。

    • 核心作用:AOP动态代理的触发点(如Spring事务的代理创建)。

    • 补充:@Transactional注解,就是通过BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)在该阶段创建代理对象,实现事务增强。

实战验证(初始化方法执行顺序)

通过代码验证初始化方法的执行顺序,以及BeanPostProcessor的增强效果:

// 定义Bean,包含三种初始化方式@ComponentpublicclassUserServiceimplementsInitializingBean {// 1. @PostConstruct注解方法(JSR-250规范,由CommonAnnotationBeanPostProcessor解析)@PostConstructpublicvoidpostConstruct() {        System.out.println("1. @PostConstruct初始化方法执行");    }// 2. InitializingBean接口方法(Spring内置接口,直接回调)@OverridepublicvoidafterPropertiesSet()throws Exception {        System.out.println("2. InitializingBean.afterPropertiesSet()执行");    }// 3. 自定义init-method(配置类中指定,通过反射调用)publicvoidinitMethod() {        System.out.println("3. 自定义init-method执行");    }}// 配置类中指定init-method@ConfigurationpublicclassAppConfig {@Bean(initMethod = "initMethod")public UserService userService() {returnnewUserService();    }}// 启动容器,输出结果(验证顺序)// 1. @PostConstruct初始化方法执行// 2. InitializingBean.afterPropertiesSet()执行// 3. 自定义init-method执行// 补充:添加BeanPostProcessor,验证增强效果@ComponentpublicclassMyBeanPostProcessorimplementsBeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if ("userService".equals(beanName)) {            System.out.println("BeanPostProcessor前置增强:userService初始化前");        }return bean;    }@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {if ("userService".equals(beanName)) {            System.out.println("BeanPostProcessor后置增强:userService初始化后");        }return bean;    }}// 新增输出(验证增强顺序)// BeanPostProcessor前置增强:userService初始化前// 1. @PostConstruct初始化方法执行// 2. InitializingBean.afterPropertiesSet()执行// 3. 自定义init-method执行// BeanPostProcessor后置增强:userService初始化后

阶段5:容器启动完成(收尾工作,Bean就绪)

核心逻辑

当所有非懒加载单例Bean实例化、初始化完成后,容器进入收尾阶段,核心是做好“清理+通知”,标记容器启动完成,此时容器中的Bean已全部就绪,可随时供应用程序调用。

收尾阶段额外初始化2个组件:       MessageSource:消息源,用于国际化。ApplicationEventMulticaster:事件广播器,用于发布容器事件。

源码片段(收尾核心方法)

// AbstractApplicationContext.refresh()中的收尾方法protectedvoidfinishRefresh() {// 1. 清除容器中的临时资源(如临时BeanDefinition、资源缓存)    clearResourceCaches();// 2. 初始化生命周期处理器(LifecycleProcessor),用于管理Bean的生命周期    initLifecycleProcessor();// 3. 触发所有Lifecycle Bean的start()方法(如定时任务、WebSocket服务)    getLifecycleProcessor().onRefresh();// 4. 发布容器启动完成事件(ContextRefreshedEvent)    publishEvent(newContextRefreshedEvent(this));// 5. 注册JVM关闭钩子,JVM退出时自动关闭容器、销毁Beanif (this.registerShutdownHook) {        registerShutdownHook();    }// 补充:标记容器状态为active,标识容器启动完成this.active.set(true);}

关键细节

  • 事件发布:

    • 容器启动完成后,发布ContextRefreshedEvent事件。

    • 若有Bean实现ApplicationListener接口,可监听该事件,执行自定义逻辑(如启动定时任务、初始化缓存)。

    • 补充:若容器有父容器,父容器、子容器启动完成后,都会分别发布该事件。

  • 关闭钩子:

    • registerShutdownHook()会注册一个JVM关闭钩子(Thread)。

    • 当JVM正常退出(如调用System.exit()、程序正常结束)时,自动调用容器的close()方法,销毁所有Bean、释放资源,避免资源泄漏。

    • 补充:手动调用context.close()方法,也会触发Bean销毁和资源释放。

  • 容器状态:

    • 启动完成后,容器状态标记为“active”,可通过context.isActive()判断容器是否正常运行。

    • 若容器关闭(close()),状态变为“inactive”,此时无法再获取Bean,否则会抛出IllegalStateException。

三、启动流程核心链路可视化(一目了然)

为了方便记忆,将整个启动流程梳理为“5阶段+核心方法”的链路,结合源码,快速掌握核心脉络:

  1. 容器初始化: new AnnotationConfigApplicationContext() → 初始化AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner → 初始化资源加载器、BeanFactory → 注册配置类

  2. BeanDefinition解析:

refresh() → prepareRefresh()(初始化环境) → obtainFreshBeanFactory() → invokeBeanFactoryPostProcessors() → 扫描Bean → 解析为BeanDefinition → 校验BeanDefinition → 存入BeanDefinitionMap

  1. Bean实例化:

finishBeanFactoryInitialization() → 初始化类型转换器 → 注册BeanPostProcessor → preInstantiateSingletons() → getBean() → createBean()(反射创建裸对象+三级缓存解决循环依赖)

  1. 依赖注入与初始化:

populateBean()(注入依赖:构造/字段/setter) → initializeBean()(Aware回调+BeanPostProcessor增强+初始化方法) → 生成成熟Bean → 存入singletonObjects

  1. 容器启动完成:

finishRefresh() → 清理临时资源 → 初始化生命周期处理器、消息源 → 发布ContextRefreshedEvent事件 → 注册关闭钩子 → 标记容器为active → 容器就绪,Bean可调用

四、实战常见坑点与解决方案(避坑必备)

结合实际开发场景,总结3个IoC容器启动过程中最常见的坑点,搭配具体解决方案,帮你快速定位、解决问题。

坑点1:Bean依赖循环,启动失败

场景

Bean A依赖Bean B,Bean B依赖Bean A,容器启动时抛出BeanCurrentlyInCreationException异常。

根源

Spring默认不支持构造方法注入的循环依赖,仅能解决setter注入/字段注入的循环依赖(通过三级缓存)。

三级缓存解决循环依赖的核心逻辑:

  1. Bean A实例化后(未注入依赖),将其工厂存入三级缓存;

  2. Bean A依赖Bean B,触发Bean B实例化;

  3. Bean B依赖Bean A,从三级缓存中获取Bean A的工厂,生成未成熟Bean(未注入依赖),存入二级缓存;

  4. Bean B注入Bean A后完成初始化,存入一级缓存;

  5. Bean A从一级缓存中获取Bean B,完成注入和初始化,存入一级缓存,打破循环。

解决方案

  • 将构造方法注入改为@Autowired字段注入或setter注入;

  • 在其中一个Bean的依赖注入处添加@Lazy注解,延迟加载Bean,打破循环依赖;

  • 重构代码,拆分Bean职责,从根源上避免循环依赖。

坑点2:Bean未被扫描,容器中找不到Bean

场景

用@Component注解标注Bean,但启动后getBean()时抛出NoSuchBeanDefinitionException异常。

根源

4种常见原因:       Bean所在包不在容器的扫描范围内;@ComponentScan注解配置错误(如basePackages写错);Bean未被正确标注注解(如漏加@Component、@Service);Bean被@Conditional注解排除。

补充:若配置类上未加@ComponentScan注解,Spring会默认扫描配置类所在包及其子包;若Bean在其他包,必须手动指定@ComponentScan的basePackages。

解决方案

  • 检查@Configuration配置类上的@ComponentScan注解,确保basePackages包含Bean所在的包;

  • Spring Boot项目,确保Bean所在包在主启动类的同级或子包下(默认扫描主启动类所在包);

  • 手动注册Bean:通过@Bean注解或context.registerBean()方法,将Bean手动注册到容器中。

坑点3:初始化方法抛出异常,容器启动中断

场景

@PostConstruct注解方法或init-method中抛出异常,导致Bean创建失败,整个容器启动中断。

根源

初始化方法是Bean创建的核心步骤,异常会直接导致Bean创建失败,进而影响容器启动。

补充:       单例且非懒加载Bean:初始化失败会导致整个容器启动中断;原型或懒加载Bean:初始化失败会在第一次获取Bean时抛出异常,不影响容器启动。

解决方案

  • 在初始化方法中添加异常捕获,避免异常向上抛出,影响容器启动;

  • 将复杂的初始化逻辑(如数据库连接、网络请求)抽离,通过@Async异步执行;

  • 检查初始化方法中的依赖Bean,避免空指针、资源未就绪等异常。

五、总结:IoC容器启动的核心本质

深挖IoC容器启动源码后会发现,其本质是“从配置中加载Bean元数据,通过反射创建Bean实例,完成依赖注入和初始化,最终将Bean纳入容器管理”的过程。

核心关键点:       refresh方法:串联所有启动步骤的“总开关”;BeanDefinition:Bean的“说明书”,定义Bean的所有核心信息;BeanPostProcessor:Bean的“增强器”,实现Spring的扩展能力(如AOP)。

补充:IoC容器启动过程中,还会执行隐性操作:BeanDefinition校验(Bean类是否存在、依赖Bean是否存在)、类型转换、循环依赖检测,这些是容器正常启动的保障。

理解IoC容器启动流程,不仅能帮你快速定位启动异常,更能让你深入理解Spring的核心机制(依赖注入、AOP、Bean生命周期),为后续学习Spring Boot自动装配、Spring Cloud打下坚实基础。

延伸学习:后续可深入研究“三级缓存解决循环依赖的底层原理”“Bean销毁流程”“Spring Boot自动装配与IoC容器的关系”“BeanDefinition的动态注册与修改”,进一步吃透Spring IoC的核心逻辑。

补充:Bean销毁流程是容器关闭时的核心操作,对应refresh方法的反向流程,主要执行@PreDestroy注解方法、DisposableBean接口方法、自定义destroy-method,释放资源。