You need to enable JavaScript to run this app. 如何在命令设计模式实现中为命令类注入Repository?
优惠活动
大模型
产品
解决方案
定价
更多

如何在命令设计模式实现中为命令类注入Repository?

问题背景

我有几个类中存在相似的else if语句,打算使用命令模式对其进行替换。核心思路是将else if的执行逻辑抽离外置,根据调用服务的不同使用对应的Repository完成操作。但我目前找不到将Repository注入到命令类的可行方式,以下通过示例详细说明:

待改造代码片段

private HashMap<String, BnResponseHandler> commandMap = new HashMap<>();

for (CustomerDelivery customerDelivery : list) {
    commandMap.put("true", new SuccessfulResponse(customerDelivery));
    commandMap.put("1", new ResponseCodeOne(customerDelivery));
    commandMap.put("2", new ResponseCodeTwo(customerDelivery));
    commandMap.put("3", new ResponseCodeThree(customerDelivery));
    BnResponseHandler bnResponseHandler = new DefaultHandler(customerDelivery);

    if(response.isSuccessful()){
        commandMap.get(String.valueOf(response.isSuccessful())).handle();
    } else if (response.getResponseCode() != null
        && !response.getResponseCode().equals("")){
        commandMap.get(response.getResponseCode()).handle();
    } else {
        bnResponseHandler.handle();
    }
} 

定义的公共接口

public interface BnResponseHandler {
    void handle();
}

接口实现类示例

@Component
public class ResponseCodeOne implements BnResponseHandler {

    @Autowired
    private FabricationRepository fabricationRepo;
    private Fabrication fabrication;

    @Autowired
    private MovementsRepository movementsRepository;
    private StockMovements stockMovements;

    @Autowired
    private CustomerDeliveryRepository customerDeliveryRepo;
    private CustomerDelivery customerDelivery;

    public ResponseCodeOne() {
    }

    public ResponseCodeOne(Fabrication fabrication) {
        this.fabrication = fabrication;
    }

    public ResponseCodeOne(StockMovements stockMovements) {
        this.stockMovements = stockMovements;
    }

    public ResponseCodeOne(CustomerDelivery customerDelivery) {
        this.customerDelivery = customerDelivery;
    }

    @Override
    public void handle() {
        if (fabrication != null) {
            fabrication.setFG_ETAT("D");
            fabrication.setLB_ETAT("");
            fabricationRepo.save(fabrication);
        } else if (stockMovements != null) {
            stockMovements.setFG_ETAT("D");
            stockMovements.setLB_ETAT("");
            movementsRepository.save(stockMovements);
        } else if (customerDelivery != null) {
            customerDelivery.setFG_ETAT("D");
            customerDelivery.setLB_ETAT("");
            customerDeliveryRepo.save(customerDelivery);
        }
    }
}

我的整体设计思路是:为所有可能接收到的响应码(本示例中为1)分别定义对应的ResponseCode处理类,在类内部实现依赖Repository Bean的业务逻辑,同时要保证灵活性,使ResponseCodeOne这类处理类可支持多种不同业务场景(即可适配Fabrication、StockMovements、CustomerDelivery三类实体)。目前遇到的问题是按上述方式实现后,Repository无法成功注入,持续抛出空指针异常。请问是否有简洁优雅的方式可以实现Repository Bean的注入?

根因分析

手动通过new关键字创建ResponseCodeOne等标注了@Component的类实例,这些实例不归Spring IoC容器管理,Spring不会自动注入@Autowired声明的Repository依赖,导致调用时触发空指针异常。

最优解决方案

推荐将业务参数和处理器逻辑拆分,处理器保持Spring单例管理,无需每次循环创建实例,性能和可维护性更好:

1. 改造公共接口

增加泛型支持,将业务实体作为handle方法的入参:

public interface BnResponseHandler<T> {
    void handle(T entity);
}

2. 改造处理器实现类

移除实体成员变量,直接在handle方法中接收参数,Repository可正常被Spring注入:

@Component
public class ResponseCodeOne implements BnResponseHandler<Object> {
    // 若三个实体有公共父类/接口,可替换Object为公共类型进一步优化
    @Autowired
    private FabricationRepository fabricationRepo;
    @Autowired
    private MovementsRepository movementsRepository;
    @Autowired
    private CustomerDeliveryRepository customerDeliveryRepo;

    @Override
    public void handle(Object entity) {
        if (entity instanceof Fabrication fabrication) {
            fabrication.setFG_ETAT("D");
            fabrication.setLB_ETAT("");
            fabricationRepo.save(fabrication);
        } else if (entity instanceof StockMovements stockMovements) {
            stockMovements.setFG_ETAT("D");
            stockMovements.setLB_ETAT("");
            movementsRepository.save(stockMovements);
        } else if (entity instanceof CustomerDelivery customerDelivery) {
            customerDelivery.setFG_ETAT("D");
            customerDelivery.setLB_ETAT("");
            customerDeliveryRepo.save(customerDelivery);
        }
    }
}

3. 改造业务调用逻辑

仅初始化一次命令映射表,无需每次循环重复创建实例和赋值:

// 注入所有Spring管理的处理器实例
@Autowired
private SuccessfulResponse successfulResponse;
@Autowired
private ResponseCodeOne responseCodeOne;
@Autowired
private ResponseCodeTwo responseCodeTwo;
@Autowired
private ResponseCodeThree responseCodeThree;
@Autowired
private DefaultHandler defaultHandler;

private HashMap<String, BnResponseHandler<?>> commandMap = new HashMap<>();

// 类初始化时仅构建一次命令映射
@PostConstruct
public void initCommandMap() {
    commandMap.put("true", successfulResponse);
    commandMap.put("1", responseCodeOne);
    commandMap.put("2", responseCodeTwo);
    commandMap.put("3", responseCodeThree);
}

// 业务循环逻辑
public void processList(List<CustomerDelivery> list, Response response) {
    for (CustomerDelivery customerDelivery : list) {
        BnResponseHandler<?> handler;
        if(response.isSuccessful()){
            handler = commandMap.get(String.valueOf(response.isSuccessful()));
        } else if (response.getResponseCode() != null && !response.getResponseCode().isBlank()){
            handler = commandMap.get(response.getResponseCode());
        } else {
            handler = defaultHandler;
        }
        handler.handle(customerDelivery);
    }
}
备选方案(保留原Handler带实体成员的写法)

如果不想调整原有结构,可将处理器改为原型作用域,通过Spring上下文获取实例:

  1. 给所有处理器类增加原型作用域注解:
@Component
@Scope("prototype")
public class ResponseCodeOne implements BnResponseHandler {
    // 原有代码不变
}
  1. 注入Spring上下文,通过上下文获取实例:
@Autowired
private ApplicationContext applicationContext;

// 循环中获取实例的代码改为
ResponseCodeOne handler = applicationContext.getBean(ResponseCodeOne.class, customerDelivery);

内容的提问来源于stack exchange,提问作者aratata

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 19:51:02