
在 spring boot 多模块项目中,手动使用 new 创建策略类实例会导致 spring 容器无法管理其生命周期,从而使得 @autowired 注入失败;正确做法是让所有策略类成为 spring bean,并通过构造函数注入依赖列表。
Spring Boot 的依赖注入(DI)机制仅对由 Spring 容器创建和管理的 Bean 生效。当你在 GameStrategySelector 中直接使用 new LocalPlayerGameStrategy() 实例化策略类时,该对象完全脱离 Spring 上下文——即使类上标注了 @Component,也不会被自动装配其字段(如 gameService、userService 等),最终导致空指针异常。
✅ 正确做法:让策略类成为托管 Bean,并依赖构造注入
首先,确保所有策略实现类均被 Spring 扫描并注册为 Bean:
@Component // 必须保留,使 Spring 能识别并托管
public class LocalPlayerGameStrategy implements BuildGameStrategy {
private final GameService gameService;
private final UserService userService;
private final PlayerService playerService;
// 构造函数注入 —— 推荐方式,保证不可变性与可测性
public LocalPlayerGameStrategy(GameService gameService,
UserService userService,
PlayerService playerService) {
this.gameService = gameService;
this.userService = userService;
this.playerService = playerService;
}
@Override
@Transactional
public void buildGame(GameDto gameDto) {
// ... 实现逻辑保持不变
}
@Override
public boolean applies(GameDto gameDto) {
return gameDto.getPlayers().stream()
.allMatch(p -> p.getPlayerType().equals(PlayerType.LOCAL.name()));
}
}同理,请为 MultiPlayerGameStrategy 和其他策略类添加构造注入,并移除所有 @Autowired 字段注解(避免与构造注入混用)。
ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有
接着,重构 GameStrategySelector,放弃手动 new 实例,改用 Spring 自动注入所有 BuildGameStrategy 实现:
@Service
public class GameStrategySelector {
private final List strategyList;
// Spring 会自动收集所有 @Component/@Service 实现 BuildGameStrategy 的 Bean
public GameStrategySelector(List allStrategies) {
this.strategyList = allStrategies;
}
public BuildGameStrategy chooseStrategy(GameDto gameDto) {
return strategyList.stream()
.filter(strategy -> strategy.applies(gameDto))
.findFirst()
.orElseGet(ErrorGameTypeStrategy::new); // 使用方法引用更安全
}
} ? 提示:ErrorGameTypeStrategy 若也需依赖注入(如日志服务),同样应声明为 @Component 并采用构造注入;若仅为无状态占位符,可保留 new 创建(因其不依赖其他 Bean)。
? 补充验证要点
- 确保 database 模块中的 @Service / @Repository 类位于 @ComponentScan 和 @EnableJpaRepositories 指定的包路径下(如 com.rydzwr.tictactoe.database),否则跨模块扫描会失败;
- 若 game 模块未显式依赖 database 模块(Maven/Gradle),请检查 pom.xml 或 build.gradle 是否已正确定义
; - 不建议在主启动类重复添加 @EnableAutoConfiguration(@SpringBootApplication 已包含),冗余注解虽不影响功能,但降低可维护性。
✅ 总结
| 问题根源 | 解决方案 |
|---|---|
| 手动 new 实例绕过 Spring 容器 | 改用构造函数注入 List |
| 字段级 @Autowired 与构造注入混用 | 统一使用构造注入,提升健壮性与测试友好性 |
| 跨模块 Bean 扫描遗漏 | 核实 @ComponentScan、@EntityScan、@EnableJpaRepositories 的 basePackages 是否覆盖全部模块包路径 |
遵循以上改造后,所有策略类将作为标准 Spring Bean 参与依赖注入,@Autowired(或更优的构造注入)即可正常工作。









