java读写分离通过自定义注解+aop+threadlocal+abstractroutingdatasource实现:注解标记读写意图,aop拦截设置线程上下文,abstractroutingdatasource在getconnection时动态路由至对应数据源。

Java 中通过注解配置读写分离,核心在于用自定义注解标记方法意图(读/写),再结合 AOP 拦截 + 动态数据源路由,在获取 Connection 前完成分发。整个过程不侵入业务逻辑,也不依赖中间件。
定义读写分离注解
声明一个运行时保留、可作用于方法和类的注解,用于显式指定数据源类型:
- 注解值支持 master(写库)或 slave(读库),也可设为默认自动识别
- 避免硬编码字符串,推荐用枚举增强可维护性:
@DataSource(DataBaseType.SLAVE) - 若未标注,默认走写库(保障安全性:没声明读,就按最保守策略处理)
用 ThreadLocal 管理当前线程的数据源上下文
每个请求线程需独立持有数据源标识,防止并发错乱:
- 定义
DataSourceContextHolder,内部用ThreadLocal<string></string>存储 key(如 "master" 或 "slave") - 提供
set()、get()、clear()方法,AOP 切面在方法执行前后调用它们 - 务必在 finally 块或 @AfterReturning/@AfterThrowing 中调用
clear(),防止线程复用导致脏状态
实现 AbstractRoutingDataSource 动态路由
Spring 提供的 AbstractRoutingDataSource 是关键桥梁,它在每次 getConnection() 时触发路由决策:
- 重写
determineCurrentLookupKey(),直接返回DataSourceContextHolder.get() - 配置时把多个真实数据源(masterDataSource、slaveDataSource)注入到
targetDataSourcesMap 中 - 设置
defaultTargetDataSource为主库,兜底保障未标注场景的写一致性
AOP 切面拦截并切换上下文
切面负责解析注解、设置上下文,并确保连接获取前已生效:
- 使用
@Around("@annotation(dataSource)")匹配带注解的方法 - 进入方法前:读取注解值 → 调用
DataSourceContextHolder.set(key) - 方法结束后:调用
DataSourceContextHolder.clear() - 注意:MyBatis 的 SqlSession、JdbcTemplate 等底层最终都调用
DataSource.getConnection(),此时路由已就绪
这套机制下,Connection 获取完全由 Spring 容器管理,代理发生在 DataSource 层而非 JDBC Driver 层,轻量且稳定。只要注解标得准、切面织入位置对、ThreadLocal 清理及时,就能可靠实现读写自动分发。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











