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
相关产品推荐
相关产品推荐

