如何让实现Runnable的类成为可动态创建Bean的Spring Boot组件
解决方案:让Spring动态构造注入GameSessionRunner实例
问题背景
需要实现一个GameSessionRunner组件(实现Runnable接口),它的实例依赖REST请求中动态生成的SudokuGame对象。当前代码直接用new创建GameSessionRunner,希望改为由Spring通过构造注入SudokuGame生成实例,后续封装成GameSession存入仓库。
具体实现步骤
1. 调整GameSessionRunner的注解与构造方法
将GameSessionRunner标记为Spring组件,并设置为原型作用域(保证每次请求生成新实例),同时通过构造方法注入所有依赖:动态生成的SudokuGame和Spring管理的NotificationService。
修改后的GameSessionRunner:
@Component @Scope("prototype") // 每次调用都生成新实例 public class GameSessionRunner implements Runnable { private final SudokuGame game; private boolean isStopped; private boolean isPaused; private final NotificationService notificationService; // 构造注入所有依赖:动态传入的SudokuGame + Spring托管的NotificationService public GameSessionRunner(SudokuGame game, NotificationService notificationService) { this.game = game; this.notificationService = notificationService; } @Override public void run() { // 业务逻辑实现 } public void pauseGame() { // 暂停逻辑实现 } public void resumeGame() { // 恢复逻辑实现 } public void stopGame() { // 停止逻辑实现 } }
2. 在GameController中获取Spring托管的GameSessionRunner实例
由于GameSessionRunner是原型作用域,不能直接用@Autowired(仅会注入一次),需借助ObjectFactory获取新实例并传入动态生成的SudokuGame。
修改后的GameController:
@RestController @RequestMapping("game-server") public class GameController { private final GameService gameService; private final GameSessionRepository gameSessionRepository; private final ObjectFactory<GameSessionRunner> gameSessionRunnerFactory; // 构造注入所需组件,包括用于获取原型实例的ObjectFactory public GameController(GameService gameService, GameSessionRepository gameSessionRepository, ObjectFactory<GameSessionRunner> gameSessionRunnerFactory) { this.gameService = gameService; this.gameSessionRepository = gameSessionRepository; this.gameSessionRunnerFactory = gameSessionRunnerFactory; } @CrossOrigin(origins = "*") @PostMapping("/create-game") public ResponseEntity<SudokuGame> createGame(@RequestBody CreateGameRequest createGameRequest) { SudokuGame game = gameService.createGame(createGameRequest); // 通过ObjectFactory获取Spring托管的GameSessionRunner实例,传入动态生成的game GameSessionRunner gameSessionRunner = gameSessionRunnerFactory.getObject(game); Thread gameThread = new Thread(gameSessionRunner); GameSession gameSession = new GameSession(gameSessionRunner, gameThread); gameSessionRepository.addGameSession(game.getGameId(), gameSession); gameThread.start(); // 启动线程执行run方法 return new ResponseEntity<>(game, HttpStatus.CREATED); } }
3. 补充说明
- 原型作用域的必要性:每个游戏会话对应唯一的
SudokuGame,因此GameSessionRunner必须设为原型作用域,确保每次请求生成独立实例。 - ObjectFactory的作用:自动处理Spring托管依赖的注入(如
NotificationService),同时支持传入动态参数(SudokuGame)。 - 线程启动:必须调用
gameThread.start(),否则run()方法不会执行。
4. 其他类保持不变
GameSession和GameSessionRepository无需修改,维持原有逻辑即可。
内容的提问来源于stack exchange,提问作者Himanshu Sajwan
相关产品推荐
相关产品推荐

