
本文详解如何脱离 Spring Boot,通过原生 Spring Framework 手动构建并启动 ApplicationContext,完成依赖注入与组件管理,帮助初学者理解 IoC 容器本质。
本文详解如何脱离 spring boot,通过原生 spring framework 手动构建并启动 `applicationcontext`,完成依赖注入与组件管理,帮助初学者理解 ioc 容器本质。
Spring Boot 极大简化了 Spring 应用的启动流程,但其封装也掩盖了底层容器初始化的关键步骤。要真正理解 Spring 的控制反转(IoC)和依赖注入(DI)机制,必须亲手搭建原生 Spring 容器——这正是本文的核心目标。
✅ 正确启动方式:使用 AnnotationConfigApplicationContext
你不能通过 new TestSpringApplication() 创建实例,因为此时该对象完全游离于 Spring 容器之外,所有 @Autowired 字段自然为 null。Spring 的依赖注入只对由容器管理的 Bean 生效。因此,必须显式创建并刷新 ApplicationContext,再从中获取受管 Bean:
package com.cypherf;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = "com.cypherf") // 显式指定扫描路径
public class TestSpringApplication {
public static void main(String[] args) {
// 1️⃣ 创建基于注解的容器上下文
try (AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(TestSpringApplication.class)) {
// 2️⃣ 从容器中获取已装配好的 Bean(推荐按类型获取)
MessageService messageService = context.getBean(MessageService.class);
MessageRepository messageRepository = context.getBean(MessageRepository.class);
// 3️⃣ 执行业务逻辑(此时所有 @Autowired 已就绪)
messageRepository.save(new Message("Message one"));
messageRepository.save(new Message("Message two"));
messageService.printAllMessages();
}
}
}
⚠️ 注意:
TestSpringApplication类本身仅作为配置类(@Configuration),不应包含业务逻辑字段(如@Autowired private MessageService)。它不是被注入的目标 Bean,而是容器的“配置蓝图”。真正的业务 Bean(如MessageService)应定义在独立的@Service、@Repository或@Component类中,并由@ComponentScan自动发现。
? 关键要点与常见误区
-
@Configuration≠ 可注入的业务类:@Configuration类用于声明 Bean 和配置元数据,其自身通常不参与 DI 链;若需在配置类中使用其他 Bean,应通过@Bean方法参数注入,而非字段注入。 -
必须显式启用组件扫描:
@ComponentScan默认只扫描当前包及其子包,建议明确指定basePackages,避免遗漏。 -
资源管理:
AnnotationConfigApplicationContext实现了AutoCloseable,推荐使用 try-with-resources 确保容器关闭时正确释放资源(如数据库连接池、线程池等)。 -
无
CommandLineRunner替代方案:Spring Boot 的CommandLineRunner是其扩展机制,原生 Spring 中可监听ContextRefreshedEvent事件实现类似效果:
@Component
public class StartupRunner implements ApplicationRunner {
private final MessageService messageService;
public StartupRunner(MessageService messageService) {
this.messageService = messageService;
}
@Override
public void run(ApplicationArguments args) {
// 启动后执行逻辑
}
}
⚠️ 但注意:ApplicationRunner 同样是 Spring Boot 特有接口。在纯 Spring 中,应改用 ApplicationListener。
? 后续进阶:手动补全基础设施(如需)
若项目涉及数据库、事务、Web 等能力,需自行注册对应 Bean:
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource(); // 示例:HikariCP 数据源
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.cypherf.model");
// ... 配置 JPA Vendor、Hibernate 属性等
return em;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
✅ 总结
脱离 Spring Boot 后,Spring 应用的启动流程回归本质三步:
-
创建容器 →
new AnnotationConfigApplicationContext(...); -
加载配置 → 通过
@Configuration+@ComponentScan声明 Bean; -
获取与使用 →
context.getBean(...)获取已注入的完整对象。
这个过程虽稍显繁琐,却清晰揭示了 Spring 的核心契约:容器即一切,Bean 由容器创建并装配,开发者只负责定义和获取。掌握它,才是深入 Spring 生态的真正起点。









