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 只会在两种情况下发生:
- ApplicationContext 已创建,但
refresh()方法尚未执行完成:BeanFactory 还没被初始化 - 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。
五、排查步骤
- 查看测试类第 11 行,确认是哪种写法
- 如果是
new ClassPathXmlApplicationContext()空构造方法,确认是否调用了refresh() - 如果是
new ClassPathXmlApplicationContext("xxx.xml"),检查配置文件路径是否正确,文件是否存在 - 如果是
context.getBean()之前调用了context.close(),调整调用顺序 - 检查
applicationContext.xml或beans.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 下 |
这个错误的本质是容器生命周期管理问题 。ApplicationContext 在 refresh() 之前只是一个空壳,只有刷新后才会创建 BeanFactory、加载 Bean 定义、完成依赖注入。调用 getBean() 前必须确保容器已经就绪,这是理解 Spring 容器生命周期的第一步。