You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于工厂模式优化跨模块服务调用方案咨询(模块C无依赖调用A/B)

问题背景与优化需求

现有模块A、B各自拥有专属服务,模块C与A、B无依赖关系,需通过工厂模式实现模块C对A、B服务的调用。
已在模块C中定义公共接口ThirdPartyCallsStrategy,供A、B模块的服务实现;当前实现的CoreCallsFactory工厂类通过反射创建对应实例,但反射方式会导致异常仅能在运行时暴露,无法在编译期发现问题。现寻求更简洁、可靠的优化实现方案。


相关代码

1. 模块C中定义的公共接口

public interface ThirdPartyCallsStrategy {
    void apply(String orderId);
}

2. 工厂类实现

@Component
@AllArgsConstructor
public class CoreCallsFactory {

    private static final String A_PACKAGE = "com.testA.service";
    private static final String A_CLASS = "TestClassA";
    
    private static final String B_PACKAGE = "com.testB.service";
    private static final String B_CLASS = "TestClassA";
    
    @SneakyThrows
    public ThirdPartyCallsStrategy createThirdPartyCallsStrategy(String type) {
        Class<?> clazz;
        if (type.equals("A")) {
            clazz = Class.forName(String.format("%s.%s", A_PACKAGE , A_CLASS ));
            return (ThirdPartyCallsStrategy) clazz.getDeclaredConstructor().newInstance();
        }

        if (type.equals("B")) {
            clazz = Class.forName(String.format("%s.%s", B_PACKAGE , B_CLASS ));
            return (ThirdPartyCallsStrategy) clazz.getDeclaredConstructor().newInstance();
        }

        return null;
    }
}

3. 模块C中调用逻辑

public String checkStatusAndRedirect(String orderId, String type) {
    boolean status = checkStatus(orderId);
    StringBuilder paymentResultUrl = new StringBuilder(frontUrl + "/public/payment/status?success=");
    
    if (status) {
        coreCallsFactory.createThirdPartyCallsStrategy(type).apply(orderId);
        paymentResultUrl.append(Boolean.TRUE);
    } else {
        paymentResultUrl.append(Boolean.FALSE);
    }
    return paymentResultUrl.toString();
}

优化方案

针对反射带来的编译期无法校验问题,推荐以下两种可靠实现方式:

方案一:Spring容器自动装配+枚举映射

步骤1:定义枚举统一服务类型

public enum ServiceType {
    A, B
}

步骤2:A、B模块实现类注册Spring Bean

模块A实现类:

@Component("serviceA")
public class TestClassA implements ThirdPartyCallsStrategy {
    @Override
    public void apply(String orderId) {
        // 模块A专属业务逻辑
    }
}

模块B实现类:

@Component("serviceB")
public class TestClassB implements ThirdPartyCallsStrategy {
    @Override
    public void apply(String orderId) {
        // 模块B专属业务逻辑
    }
}

步骤3:改造工厂类,通过Spring容器获取实例

@Component
public class CoreCallsFactory {

    private final Map<String, ThirdPartyCallsStrategy> strategyMap;

    // Spring自动注入所有ThirdPartyCallsStrategy类型的Bean,key为Bean名称
    public CoreCallsFactory(Map<String, ThirdPartyCallsStrategy> strategyMap) {
        this.strategyMap = strategyMap;
    }

    public ThirdPartyCallsStrategy createThirdPartyCallsStrategy(ServiceType type) {
        return switch (type) {
            case A -> strategyMap.get("serviceA");
            case B -> strategyMap.get("serviceB");
            default -> throw new IllegalArgumentException("不支持的服务类型: " + type);
        };
    }
}

核心优势:

  • 编译期即可校验Bean是否存在,彻底避免运行时反射错误
  • 无需手动维护类路径,Spring自动管理Bean生命周期
  • 枚举替代字符串参数,从源头避免拼写错误

方案二:SPI(服务提供者接口)机制

步骤1:配置SPI文件

在模块C的resources/META-INF/services目录下创建文件com.testC.ThirdPartyCallsStrategy,文件内容为A、B模块实现类的全类名:

com.testA.service.TestClassA
com.testB.service.TestClassB

步骤2:改造工厂类,通过ServiceLoader加载实例

@Component
public class CoreCallsFactory {

    private final Map<String, ThirdPartyCallsStrategy> strategyMap;

    public CoreCallsFactory() {
        strategyMap = new HashMap<>();
        ServiceLoader<ThirdPartyCallsStrategy> loader = ServiceLoader.load(ThirdPartyCallsStrategy.class);
        for (ThirdPartyCallsStrategy strategy : loader) {
            // 根据实现类类名映射服务类型,也可自定义注解实现更灵活的映射
            String type = strategy.getClass().getSimpleName().contains("A") ? "A" : "B";
            strategyMap.put(type, strategy);
        }
    }

    public ThirdPartyCallsStrategy createThirdPartyCallsStrategy(String type) {
        ThirdPartyCallsStrategy strategy = strategyMap.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的服务类型: " + type);
        }
        return strategy;
    }
}

核心优势:

  • 模块C与A、B完全解耦,无需依赖A、B的Jar包即可编译
  • SPI文件可在编译期校验实现类的合法性
  • 扩展新服务只需添加SPI条目,无需修改工厂类代码

调用逻辑改造建议

将原有的字符串type参数替换为枚举类型,进一步提升代码健壮性:

public String checkStatusAndRedirect(String orderId, ServiceType type) {
    boolean status = checkStatus(orderId);
    StringBuilder paymentResultUrl = new StringBuilder(frontUrl + "/public/payment/status?success=");
    
    if (status) {
        ThirdPartyCallsStrategy strategy = coreCallsFactory.createThirdPartyCallsStrategy(type);
        strategy.apply(orderId);
        paymentResultUrl.append(Boolean.TRUE);
    } else {
        paymentResultUrl.append(Boolean.FALSE);
    }
    return paymentResultUrl.toString();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:50:00