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

Java工厂模式如何消除if判断 实现关键字动态匹配对应实现类

Java可扩展Action工厂实现方案

你遇到的静态代码块不生效的核心原因是Java类采用懒加载机制:只要类没有被主动引用(比如实例化、调用静态方法/属性、反射加载),类就不会完成初始化,写在类里的静态注册代码块自然不会执行,手动维护类加载列表本质和写if分支没有区别。要实现新增实现类无需修改工厂代码的自动注册效果,可根据项目场景选择以下成熟方案:

方案1:JDK原生SPI(无第三方依赖)

SPI是JDK内置的服务发现机制,专门用来做接口实现类的自动发现,落地步骤:

  • 首先调整Action接口定义:Java接口不支持强制实现类提供静态方法,因此把获取关键字的方法下沉为接口的实例方法:
public interface Action {
    void invoke();
    // 每个实现类返回自己绑定的关键字
    String getKeyword();
}
  • 改造工厂类,初始化时通过ServiceLoader加载所有Action实现,建立关键字和实例生成器的映射:
import java.util.ServiceLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class ActionFactory {
    private static final Map<String, Supplier<Action>> ACTION_MAPPING = new HashMap<>();

    static {
        ServiceLoader<Action> actionLoader = ServiceLoader.load(Action.class);
        for (Action actionInstance : actionLoader) {
            // 存实例生成器,避免提前初始化所有实现类
            ACTION_MAPPING.put(actionInstance.getKeyword(), () -> {
                try {
                    return actionInstance.getClass().getDeclaredConstructor().newInstance();
                } catch (Exception e) {
                    throw new IllegalArgumentException("Action实例化失败: " + actionInstance.getKeyword(), e);
                }
            });
        }
    }

    public static Action getInstance(String actionKey) {
        Supplier<Action> actionSupplier = ACTION_MAPPING.get(actionKey);
        return actionSupplier == null ? null : actionSupplier.get();
    }
}
  • 按照SPI规范配置实现类列表:在项目resources/META-INF/services目录下,创建名为Action接口全限定名的文件(比如接口包路径为com.xxx.biz.Action,文件名就为com.xxx.biz.Action),文件内逐行写每个实现类的全限定名:
com.xxx.biz.impl.ActionOpen
com.xxx.biz.impl.ActionClose

该方案优点是纯JDK实现无额外依赖,新增实现类仅需添加实现代码+在SPI配置文件追加一行,无需修改工厂类逻辑,符合开闭原则。

方案2:类路径扫描(适合带IoC容器的项目,零配置)

如果项目已经集成Spring等IoC容器,不需要手动写配置,直接利用容器的扫描能力即可实现完全自动注册:

  • 所有Action实现类添加@Component注解标记为容器管理的Bean,正常实现Action接口的getKeyword()方法即可。
  • 工厂类实现ApplicationContextAware接口,在容器启动时自动收集所有Action类型的Bean,建立关键字映射:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

@Component
public class ActionFactory implements ApplicationContextAware {
    private final Map<String, Action> actionMap = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 拿到容器中所有Action类型的Bean
        Map<String, Action> actionBeans = applicationContext.getBeansOfType(Action.class);
        actionBeans.values().forEach(action -> actionMap.put(action.getKeyword(), action));
    }

    public Action getInstance(String actionKey) {
        Action action = actionMap.get(actionKey);
        // 如果需要每次返回新实例,把Action设置为prototype作用域,这里通过applicationContext.getBean获取即可
        return action;
    }
}

如果是非Spring项目,也可以引入Reflections这类轻量字节码扫描库,在工厂静态初始化块里扫描指定包路径下所有实现Action接口的类,遍历完成注册,不需要写任何额外配置。

方案3:编译期自动生成配置(零运行时开销)

如果既不想手动维护SPI配置文件,也不想引入运行时扫描的性能开销,可以自定义一个@ActionReg注解,搭配Java注解处理器,在代码编译阶段自动扫描所有标注了注解的Action实现类,自动生成SPI配置文件。开发时只需要在实现类上加注解、指定绑定的关键字即可,运行时逻辑和原生SPI完全一致,没有额外性能损耗。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:03:18