毫秒级物理变轨指借助AOP在方法执行前微秒至毫秒内完成数据源路由决策,通过自定义注解(如@SwitchDataSource("slave_readonly"))传入库别名,AOP环绕通知将其写入ThreadLocal,AbstractRoutingDataSource的determineCurrentLookupKey()实时返回该别名以精准命中配置的物理数据源。

“毫秒级物理变轨”不是标准技术术语,这里应理解为:在动态多数据源切换场景中,借助 AOP 在方法执行前极短时间内(微秒至毫秒级)完成数据源路由决策,并准确命中注解所指定的库别名(如 master、slave01、db_order 等),从而实现无感知、低延迟、线程安全的物理数据源切换。
注解定义需明确携带库别名
自定义注解必须支持传入可识别的库标识符,这是后续路由的唯一依据:
- 使用 @Target({METHOD, TYPE}) 允许标注在方法或类上
- 用 @Retention(RUNTIME) 确保运行期可通过反射读取
- 定义 String value() default "master" 或 String key() default "" 字段,用于显式声明目标库别名
- 示例:@SwitchDataSource("slave_readonly") 或 @RouteTo("db_goods")
AOP 切面在环绕通知中提取并绑定别名
切面不执行 SQL,只做「路由前置决策」——在目标方法调用前,把注解值写入当前线程上下文:
- 使用 @Around 拦截带注解的方法,避免前置/后置分离导致的时序风险
- 通过 joinPoint.getSignature() 获取方法签名,再调用 getMethod().getAnnotation(...) 提取注解实例
- 调用 DynamicDataSource.setDataSourceKey(anno.value()) 将别名存入 ThreadLocal
- 务必在 proceed() 执行后调用 clearDataSourceKey(),防止线程复用污染
AbstractRoutingDataSource 实时响应别名
Spring 的 AbstractRoutingDataSource 是路由中枢,其 determineCurrentLookupKey() 方法被 MyBatis/ JDBC 在创建 Connection 前高频调用:
- 该方法直接返回 ThreadLocal.get() 的值,无任何缓存或异步延迟
- 返回值必须与配置中 targetDataSources 的 Map Key 完全一致(大小写敏感、不可含空格)
- 若返回 null 或未匹配 key,会触发默认数据源或抛出 IllegalStateException,需提前校验
关键保障点:轻量、同步、无副作用
所谓“毫秒级”,本质是消除中间环节和阻塞操作:
- 不查数据库、不调远程服务、不走 Spring EL 表达式解析
- 不依赖 Spring Context Refresh 或 Bean 初始化时机
- ThreadLocal 读写是 JVM 级原语,开销在纳秒级;整个切换链路通常
- 确保所有 DAO 层调用都经过同一 DataSource Bean(即 DynamicRoutingDataSource 实例)

















