Spring 启动报错:BeanFactory not initialized 的原因与解决

Spring 启动报错:BeanFactory not initialized 的原因与解决

一、错误现象

测试类运行时抛出异常:

复制代码
java.lang.IllegalStateException: BeanFactory not initialized or already closed 
- call 'refresh' before accessing beans via the ApplicationContext
	at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory
	at org.springframework.context.support.AbstractApplicationContext.getBean
	at com.xie.spring.testFirstBean.test(testFirstBean.java:11)

错误发生在测试类的第 11 行,调用 getBean() 时。

二、错误含义

这个异常来自 AbstractRefreshableApplicationContext.getBeanFactory()。当该方法被调用时,内部的 beanFactory 字段为 null,于是抛出此异常。

beanFactory 字段为 null 只会在两种情况下发生:

  1. ApplicationContext 已创建,但 refresh() 方法尚未执行完成:BeanFactory 还没被初始化
  2. ApplicationContext 已经被 close() 关闭:BeanFactory 被销毁,引用被置为 null

简单说:容器还没启动,或者容器已经关闭,你就去调用 getBean() 获取 Bean 了。

三、最可能的原因:手动创建容器但忘了加载配置

结合你的项目结构(spring-001-first),测试代码很可能是这样写的:

java 复制代码
public class testFirstBean {
    @Test
    public void test() {
        // ❌ 错误:只 new 了容器,没有指定配置文件,也没有调用 refresh()
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        MyBean bean = context.getBean("myBean", MyBean.class);
    }
}

new ClassPathXmlApplicationContext() 空构造方法只是创建了对象,并没有加载配置文件 。构造函数中如果没有传入配置文件路径,需要手动调用 setConfigLocation()refresh()

正确写法一:通过构造方法传入配置文件

java 复制代码
@Test
public void test() {
    // ✅ 正确:构造方法会自动调用 refresh()
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    MyBean bean = context.getBean("myBean", MyBean.class);
    System.out.println(bean);
}

ClassPathXmlApplicationContext(String... configLocations) 这个构造方法内部会自动执行 refresh(),BeanFactory 会被初始化,之后才能正常获取 Bean。

正确写法二:手动调用 refresh()

java 复制代码
@Test
public void test() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
    context.setConfigLocation("beans.xml");  // 设置配置文件
    context.refresh();                        // 手动刷新容器
    MyBean bean = context.getBean("myBean", MyBean.class);
    System.out.println(bean);
    context.close();                          // 用完关闭
}

四、另一种情况:容器已关闭

如果测试代码先关闭了容器,然后又调用 getBean(),也会触发同样的错误:

java 复制代码
@Test
public void test() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    context.close();  // 关闭容器
    MyBean bean = context.getBean("myBean", MyBean.class);  // ❌ 抛异常
}

close() 之后,容器内的 BeanFactory 被销毁,无法再获取 Bean。

五、排查步骤

  1. 查看测试类第 11 行,确认是哪种写法
  2. 如果是 new ClassPathXmlApplicationContext() 空构造方法,确认是否调用了 refresh()
  3. 如果是 new ClassPathXmlApplicationContext("xxx.xml"),检查配置文件路径是否正确,文件是否存在
  4. 如果是 context.getBean() 之前调用了 context.close(),调整调用顺序
  5. 检查 applicationContext.xmlbeans.xml 中是否包含 Bean 定义

六、配置文件路径的注意事项

Maven 项目 中,配置文件通常放在 src/main/resources 目录下:

复制代码
src/main/resources/
├── beans.xml              → classpath:beans.xml
├── applicationContext.xml → classpath:applicationContext.xml
└── config/
    └── spring.xml         → classpath:config/spring.xml

如果配置放在子目录中,路径要包含子目录:

java 复制代码
// 配置文件在 src/main/resources/config/spring.xml
ApplicationContext context = new ClassPathXmlApplicationContext("config/spring.xml");

七、Spring Boot 中不需要手动 refresh

如果你后续使用 Spring Boot,不需要手动创建 ApplicationContext,启动时框架会自动完成 refresh()

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // SpringApplication.run 内部自动调用 refresh()
        SpringApplication.run(Application.class, args);
    }
}

但在纯 Spring(非 Spring Boot)的测试中,必须显式创建容器并触发 refresh。

八、最佳实践

java 复制代码
public class testFirstBean {
    
    private ClassPathXmlApplicationContext context;
    
    @Before
    public void setUp() {
        // 在测试方法执行前初始化容器
        context = new ClassPathXmlApplicationContext("beans.xml");
    }
    
    @After
    public void tearDown() {
        // 在测试方法执行后关闭容器
        if (context != null) {
            context.close();
        }
    }
    
    @Test
    public void test() {
        // 此时容器已经就绪,可以直接获取 Bean
        MyBean bean = context.getBean("myBean", MyBean.class);
        Assert.assertNotNull(bean);
    }
}

@Before 方法在每个测试方法执行前运行,确保容器已初始化;@After 方法在测试结束后关闭容器,释放资源。这样每个测试方法都在干净的容器中执行,互不干扰。

九、总结

原因 表现 解决方案
使用空构造方法创建容器 new ClassPathXmlApplicationContext() 后直接 getBean() 传入配置文件路径或手动调用 refresh()
容器已关闭 close() 后继续 getBean() 调整代码顺序,不要关闭后访问
配置路径错误 容器创建时找不到 XML 文件 检查 XML 路径,确保在 src/main/resources

这个错误的本质是容器生命周期管理问题ApplicationContextrefresh() 之前只是一个空壳,只有刷新后才会创建 BeanFactory、加载 Bean 定义、完成依赖注入。调用 getBean() 前必须确保容器已经就绪,这是理解 Spring 容器生命周期的第一步。

相关推荐
步行cgn1 小时前
Spring 报错:No bean class specified on bean definition 的原因与解决
java·后端·spring
quantdash_cc2 小时前
股票历史数据为什么比实时行情更重要?回测结果失真的根源可能就在 K 线数据
开发语言·python·数据分析·量化交易·股票数据·quantdash
zhangjw342 小时前
第47篇:Java综合实战:电商秒杀系统(高并发场景)
java·开发语言
土司大王2 小时前
LeetCode hot100 回溯专题总结:Java 通用模板、决策树模型
java·算法·leetcode·决策树
Web3&Basketball2 小时前
LLM 峰谷定价怎么吃满:一个能对账的错峰调度器
python·性能优化·大模型·任务调度·成本优化·deepseek·推理优化
2601_962218612 小时前
万象生鲜系统订单全生命周期状态同步技术实现业务可视
大数据·数据库·人工智能·python·算法
FOAF-lambda2 小时前
uiautomation-new 控件方位的使用
开发语言·python
重生之我是Java开发战士2 小时前
【Spring Cloud】Nacos注册中心
后端·spring·spring cloud
OKkankan2 小时前
LangChain能力详解!:从工具调用到 LangSmith——让聊天模型具备实时交互能力!
开发语言·python·langchain