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.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 容器生命周期的第一步。

相关推荐
金銀銅鐵9 小时前
[Java] 用 CLI 展示 EnumSet 增减元素时的内部变化(下)
java·后端·ai编程
小小龙学IT9 小时前
Python Polars 高性能数据处理引擎深度解析
开发语言·python·rust·1024程序员节
Nuanyt9 小时前
Hot 100 刷题笔记 · 数组与哈希专题(3 题)
java·笔记·算法·leetcode·哈希算法·1024程序员节
csdn_aspnet9 小时前
JavaScript 未排序数组中第 k 个最小/最大元素 (预期线性时间)
python
quantdash_cc9 小时前
如何利用标的信息为研究股票池补充名称和交易所信息?
开发语言·python·pandas·量化交易·1024程序员节·股票数据·quantdash
数字化转型分享点滴9 小时前
机加工企业上大数据分析系统有哪些容易踩的坑?
python·1024程序员节
xieliyu.9 小时前
【JavaEE进阶】Spring Boot 日志:从 System.out 到 @Slf4j,6 个级别 + 4 类配置一篇讲透
java·spring boot·spring·java-ee·springboot·logback·slf4j
B2_Proxy10 小时前
Scrapy 代理 IP 调度进阶:基于 Redis 与中间件的高可用架构实战
python·scrapy·1024程序员节
在世修行10 小时前
智学文枢:AI 客户端封装
python·http·ai·1024程序员节·pyside
IT_Octopus10 小时前
Apollo 配置中心入门:一所“小学校“读懂 Portal、Meta Server、Config Service
java·后端·spring