核心是调用方只依赖接口类型,彻底解除编译期耦合;接口须窄而稳、职责单一,参数用不可变dto,运行时注入实现,插件通过统一上下文获取能力并遵循生命周期管理。

核心是让调用方只认接口类型,不感知任何具体实现类——方法参数用接口,不是为了“写得规范”,而是为了彻底切断编译期依赖,让新插件加进来时,主流程一行代码都不用改。
接口参数必须窄而稳,聚焦单一语义
方法入参声明为接口类型,前提是这个接口本身足够轻、职责明确。比如日志功能,不要定义一个 LogService 接口塞进 8 个方法,而应拆成 EventLogger(记录业务事件)、ErrorReporter(上报异常)两个独立接口。每个接口只暴露 1~2 个方法,如 log(String event, Map
- 参数尽量用不可变 DTO 或值对象,比如传 BookingRequest,而不是 Map
或裸 long ID - 避免在接口里定义常量或默认方法承载核心逻辑;Java 8 的 default 方法仅用于兼容性补丁
- 接口发布后尽量不动;需扩展时优先用新增子接口(如 AsyncEventLogger extends EventLogger),而非修改原方法签名
运行时注入实现,编译期零耦合
主程序的方法签名里写的是接口类型,但实际执行的是哪个类,由加载器在运行时决定。关键不在“有没有接口”,而在“调用方是否真的一点也不知道实现是谁”。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- Spring 环境下,用 @Autowired 注入接口类型,配合 @ConditionalOnProperty 或自定义 @ImportSelector 控制插件开关
- 无框架场景,用 Java SPI:插件 JAR 在 META-INF/services/com.example.EventLogger 文件中写入实现类全名,主程序通过 ServiceLoader.load(EventLogger.class) 获取实例
- 所有插件必须实现统一生命周期方法(initialize() / shutdown()),由宿主统一管理,失败时跳过并记录警告,不中断主流程
上下文传递能力,避免插件自行拉取依赖
插件不能自己 new 日志、查配置、连缓存——这会让它和基础设施强绑定。所有外部能力必须通过构造函数或初始化方法注入。
- 宿主创建插件实例时,传入一个 PluginContext 对象,里面封装 ConfigReader、MetricsReporter 等细粒度接口
- PluginContext 本身遵循接口隔离原则,不提供大而全的 ApplicationContext;旧插件用不到的新能力,新版本上下文可安全添加
- 插件内部不持静态状态、不依赖单例,所有共享数据都靠上下文传入,方便单元测试和并发执行
预留钩子与组合能力,支撑复杂协作
纯接口调用还不够,真实业务需要插件参与流程而不侵入主逻辑。这时靠组合+钩子,不是靠继承或修改接口。
- 主流程关键节点预留回调,比如 beforeProcess()、afterCommit(),插件选择性实现
- 多个插件协同时,用组合模式:一个 OrderHandler 接口背后,可组合 PaymentValidator + InventoryReserver + NotificationSender,各司其职
- 返回值统一用语义明确的类型,如 Result.success() 或 Result.fail(ErrorCode.VALIDATION_ERROR, "库存不足"),不抛未声明的运行时异常
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










