多态是插件式架构解耦的底线,主业务代码只认接口、不知具体实现;接口需小而稳,运行时动态绑定,加载与生命周期由宿主统一托管,禁止硬编码和类型判断。

多态不是插件架构的“加分项”,而是解耦的底线——主业务代码必须只认接口,完全不知道插件是哪个类、在哪个 jar 里、用什么方式加载。
接口要小而稳,只说“做什么”
定义一个极简接口,比如:
-
只暴露核心行为:如
log(Level level, String msg)和flush(),不掺杂配置读取、线程池创建、HTTP 客户端初始化等实现细节 -
参数用 DTO 或不可变类型:避免传
ArrayList,改用List<logentry></logentry>;入参封装成LogRequest类,字段明确、不可变 -
返回值语义清晰:统一返回
Result.success()或Result.fail(ErrorCode, "msg"),不抛 unchecked 异常穿透调用链 -
接口发布后尽量不动:新增能力优先用默认方法(JDK 8+),或定义子接口(如
AsyncLogger extends Logger)
运行时绑定,编译期零依赖
业务代码里不能出现任何具体插件类名:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
变量声明必须是接口类型:写
Logger logger = pluginLoader.get("file-logger");,而不是FileLogger logger = ... -
禁止 instanceof 和强转:一旦写了
if (plugin instanceof DbLogger),多态就失效了,说明设计退化为条件分支 -
工厂/加载器返回值只能是接口:
PluginLoader.load(String id)返回Plugin,不是Class>或字符串类名 -
插件标识用逻辑名:配
"log-elk",而不是"com.example.ElkLogger",防止编译期硬编码
加载与生命周期由宿主统一托管
插件不是 new 出来的,是被“纳管”的:
-
按需加载,非启动全量:通过配置开关(YAML 中
enabled: true)或运行时事件触发加载 -
标准三阶段生命周期:宿主调用
plugin.initialize(context)→plugin.execute(input)→plugin.shutdown(),不许插件自己开线程、启定时器 -
失败隔离:某个插件
initialize()报错,只记录警告并跳过,不影响其他插件加载和主流程执行 -
上下文注入代替全局获取:不许插件里写
SpringContext.getBean(Logger.class),宿主创建实例时传入PluginContext,里面只含它真正需要的能力(如ConfigReader、MetricsReporter)
动态加载需绕过默认类加载机制
要支持外部 jar 热插拔,得自己管类加载:
-
用 URLClassLoader 加载插件 jar:构造时传入插件路径的
URL,并显式指定父加载器为宿主的类加载器(确保接口类由宿主提供) -
切断双亲委派:重写
loadClass(),优先从插件路径找类;仅对java.、javax.等系统包才委派给父加载器,避免宿主类覆盖插件同名依赖 -
资源访问走插件自己的 ClassLoader:读配置用
pluginClassLoader.getResourceAsStream("config.json"),不用当前类的getClass().getResourceAsStream() -
卸载 = 丢弃引用 + 清理资源:不再持有该
URLClassLoader实例;插件内关闭连接、注销监听器、停止线程;无强引用残留,GC 后整个插件类空间可回收
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










