核心是主程序仅依赖稳定小接口,运行时通过配置或serviceloader动态加载实现类并强转为接口引用,接口方法不删只扩,插件按initialize/execute/shutdown生命周期托管,能力通过组合注入而非继承。

核心是让主程序只依赖接口,运行时才决定加载哪个实现——不是编译期写死类型,而是靠配置或约定,在启动或调用时动态选类、实例化、转成接口引用。
接口要小而稳,只定义“做什么”
比如日志插件只暴露 log(Level, String) 和 flush(),不掺杂配置读取、线程池初始化等细节。这样新增一个发 HTTP 的实现,老代码完全不用改。接口一旦发布,尽量别删方法;要扩展可用 default 方法(Java 8+),或拆新子接口如 AsyncLogger extends Logger。
加载必须走运行时,避开编译耦合
不能在业务代码里写 new FileLogger() 或 instanceof 判断类型。正确做法是:
- 用配置指定插件名,如 "log-http",而非全限定类名
- 通过 ServiceLoader(META-INF/services)自动发现,或用 URLClassLoader 加载插件 JAR,但必须把宿主类加载器设为 parent,确保接口类型由宿主提供
- 反射创建实例后,强转为接口类型:(Logger) instance,而不是具体类
实例统一由宿主托管,不裸奔
插件不是一加载就执行,而是走标准生命周期:initialize() → execute() → shutdown()。宿主负责:
- 按需加载:只在第一次调用前初始化,非全部启动时加载
- 注入上下文:把 ConfigReader、MetricsReporter 等能力封装进 PluginContext,构造时传入,不许插件自己去 Spring 容器拿 Bean
- 异常隔离:插件执行出错要捕获封装,不抛 unchecked exception 穿透主线程
组合优于继承,行为靠接口注入
不要让插件类层层继承来叠加功能。校验逻辑交给 Validator 接口,通知逻辑交给 Notifier 接口,存储交给 StorageAdapter 接口——每个都可独立替换。插件内部只持 final 字段,构造器注入,调用一律走 this.notifier.notify(msg),不暴露实现细节。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











