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

相关推荐
LOVE️YOU5 小时前
Python 中 Tuple 为什么有时可 Hash,有时不可 Hash?
开发语言·python·哈希算法
ym hyd 1115 小时前
试题库管理系统源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
小oo呆5 小时前
【学习心得】迭代器和可迭代对象
开发语言·python
AC赳赳老秦5 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
钱栈up5 小时前
mvn compile 卡死在 javac 阶段:一次类型不匹配的排查与修复
java·开发语言
迅猛龙办公室5 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
吠品7 小时前
CSS图片模糊过渡:一个transition加filter就够
java·数据库·notepad++
XiaoMaqqqq7 小时前
知名的IP驱动产业新场景新工具
网络·python·网络协议·tcp/ip
言乐67 小时前
Python实现语音审核模型
开发语言·python·django·virtualenv·pygame
步行cgn7 小时前
Spring 全注解开发详解
java·后端·spring