Spring中RestController如何调用其他组件处理对象并保存至另一仓库?
问题分析
你遇到的核心问题是:手动new出来的SimulationProcess对象并不受Spring容器管理,所以里面通过@Autowired注入的SecondRepository会是null,根本没法执行保存操作。Spring的依赖注入只对它自己创建并管理的Bean生效,手动实例化的对象不在这个范围内。
下面给你两种可行的解决方案,分别对应不同的场景:
方案1:把SimulationProcess改成无状态的Spring Service(推荐)
这种方式是Spring里最常用的做法,把SimulationProcess设计成无状态的单例Bean,通过方法参数传递Simulation对象,而不是把它存在实例变量里。这样既符合Spring的设计理念,又保证线程安全。
首先修改SimulationProcess:
// 用@Service标记让Spring把它管理成单例Bean @Service public class SimulationProcess { // 用构造器注入依赖,Spring 4.3+可以不用写@Autowired private final SecondRepository secondRepository; public SimulationProcess(SecondRepository secondRepository) { this.secondRepository = secondRepository; } // 把Simulation作为方法参数传入,而不是存在实例变量中 public void simulate(Simulation simulation) { // 处理simulation中的数据,比如提取字段创建Second对象 Second secondEntity = new Second(); // 假设Second有个simulationId字段,对应Simulation的id secondEntity.setSimulationId(simulation.getId()); // 其他字段处理... secondRepository.save(secondEntity); } }
然后在你的SimulationController里注入这个Service:
@RestController @RequestMapping("/api") public class SimulationController { private final SimulationRepository simulationRepository; private final SimulationProcess simulationProcess; // 构造器注入两个Repository和Service,避免字段注入的弊端 public SimulationController(SimulationRepository simulationRepository, SimulationProcess simulationProcess) { this.simulationRepository = simulationRepository; this.simulationProcess = simulationProcess; } @PostMapping("/simulations") public Simulation createSimulation(@Valid @RequestBody Simulation simulation) { Simulation savedSimulation = simulationRepository.save(simulation); // 直接调用service的方法,传入保存后的simulation(此时已经有id了) simulationProcess.simulate(savedSimulation); return savedSimulation; } }
方案2:如果需要有状态的SimulationProcess(原型Bean)
如果你确实需要每个请求对应一个独立的SimulationProcess实例(比如要在实例中保存一些请求相关的状态),可以把它标记为原型作用域(prototype),然后通过Spring的工厂来获取实例,这样Spring会帮你注入依赖。
首先修改SimulationProcess:
@Component // 标记为原型作用域,每次获取都会创建新实例 @Scope("prototype") public class SimulationProcess { private final Simulation simulation; private final SecondRepository secondRepository; // 构造器同时注入依赖和Simulation对象 public SimulationProcess(Simulation simulation, SecondRepository secondRepository) { this.simulation = simulation; this.secondRepository = secondRepository; } public void simulate() { // 直接使用实例中的simulation处理数据 Second secondEntity = new Second(); secondEntity.setSimulationId(simulation.getId()); // 其他处理... secondRepository.save(secondEntity); } }
然后在Controller里用ObjectFactory来获取原型实例:
@RestController @RequestMapping("/api") public class SimulationController { private final SimulationRepository simulationRepository; private final ObjectFactory<SimulationProcess> simulationProcessFactory; public SimulationController(SimulationRepository simulationRepository, ObjectFactory<SimulationProcess> simulationProcessFactory) { this.simulationRepository = simulationRepository; this.simulationProcessFactory = simulationProcessFactory; } @PostMapping("/simulations") public Simulation createSimulation(@Valid @RequestBody Simulation simulation) { Simulation savedSimulation = simulationRepository.save(simulation); // 通过ObjectFactory获取原型实例,并传入savedSimulation SimulationProcess simulationProcess = simulationProcessFactory.getObject(savedSimulation); simulationProcess.simulate(); return savedSimulation; } }
为什么原来的方式不行?
再强调一下:当你手动new SimulationProcess(simulation)时,这个对象完全脱离了Spring的控制。Spring不会自动注入它里面的SecondRepository,所以调用simulate()方法时会抛出NullPointerException。只有Spring自己创建的Bean(比如通过@Service、@Component标记,或者XML配置的),才会触发依赖注入流程。
内容的提问来源于stack exchange,提问作者Sikorkaa

