核心是用注解作路由标记,配合专属classloader隔离与接口契约分层实现版本逻辑天然互不感知:@versioned仅标记版本,由pluginclassloader按路径加载对应实现,调用方通过接口动态装配,杜绝类污染与静态依赖。

核心不是“强行加注解”,而是用注解作触发开关,配合类加载器隔离 + 接口契约分层,让不同版本逻辑天然互不感知。
注解只做路由标记,不承载实现
定义一个轻量级注解,比如 @Versioned(version = "2.1"),它不做任何校验、不解析参数、不调用方法——仅作为AOP切入点或工厂选择依据:
- 作用目标限于类或方法(
@Target({TYPE, METHOD})) - 生命周期为运行时(
@Retention(RUNTIME)) - 不带复杂属性,避免反射开销和配置歧义
- 注解本身打包进基础组件jar,由系统类加载器统一加载
版本逻辑必须由专属ClassLoader加载
光有注解没用。真正隔离靠的是:每个版本的业务类(如 OrderProcessorV21、OrderProcessorV30)必须由各自独立的 PluginClassLoader 实例加载:
- 构造加载器时绑定唯一路径:
new PluginClassLoader("/lib/order/v2.1/") - 重写
loadClass:先查本地路径,找不到再委托(避开双亲委派污染) -
OrderProcessorV21和OrderProcessorV30即使全限定名相同,也是JVM眼中完全不同的类 - 它们的静态变量、类型检查、方法区数据全部物理隔离
统一接口 + 运行时动态装配
调用方永远只依赖抽象,不碰具体实现:
- 在公共模块定义
OrderService接口,由AppClassLoader加载 - v2.1 的实现类
OrderServiceImpl打在order-core-2.1.jar中,由其专属加载器加载 - 根据注解中的
version值,从缓存或工厂中取出对应 ClassLoader,再通过Class.forName("...", true, loader)获取实现类 - 用接口类型接收实例:
OrderService service = (OrderService) constructor.newInstance()
避免常见陷阱
几个关键点一旦出错,隔离即失效:
- 不要复用同一个 ClassLoader 实例去加载多个版本
- 不要把不同版本的实现类打在同一个jar里
- 不要在实现类中直接 new 其他版本的类或引用其静态字段
- 日志、监控、线程上下文等横切逻辑,需确保不跨加载器传递原始对象(建议传DTO或字符串标识)










