签入版本

This commit is contained in:
2026-09-10 21:41:50 +08:00
commit 717116a605
2693 changed files with 355025 additions and 0 deletions
@@ -0,0 +1,261 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | Copyright (c) 2020-2026 小蚂蚁云团队 版权所有
// +----------------------------------------------------------------------
// | 官方平台: https://www.xiaomayicloud.com
// +======================================================================
// | 【知识产权声明】
// | 1. 权属声明:本软件是受《中华人民共和国著作权法》
// | 保护的专有财产,完整版权归小蚂蚁云团队所有
// | 2. 权利范围:所有知识产权(商标、专利、著作权、
// | 商业秘密等)均归小蚂蚁云团队所有,受法律保护
// | 3. 授权性质:用户仅可获得有限的、非独占的使用许可
// | 4. 禁止行为:未经书面授权,禁止对本软件本身
// | 申请任何知识产权
// | 5. 权利保留:未在本协议中明示授予的一切权利
// | 均由小蚂蚁云团队保留
// +======================================================================
// | 【授权状态说明】
// | 1. 未授权状态:用户未购买有效的商业许可证
// | 2. 商业授权:已购买正式商业许可证的用户
// +======================================================================
// | 【绝对禁止条款】
// | 无论授权状态如何,任何用户均严格禁止以下行为:
// | 1. 逆向工程:对软件进行反编译、反汇编、技术破解或任何形式的逆向分析
// | 2. 版权破坏:移除、修改、遮挡软件中的版权标识、许可控制或技术保护措施
// | 3. 非法传播:复制、分发、出租、出借、转售、再许可本软件或其许可权利
// | 4. 竞争性使用:利用本软件的代码、架构、设计开发直接竞争的产品或服务
// | 5. 侵权使用:将软件用于侵犯第三方知识产权或其他合法权益的用途
// +======================================================================
// | 【商业授权许可范围】
// | 购买有效商业许可证的用户,在遵守上述禁止条款的前提下,获得:
// | 1. 使用权利:在许可证约定的项目数量、开发者数量范围内使用本软件
// | 2. 技术支持:商业授权用户可以获取本地开发部署的技术支持服务
// | 3. 版本更新:在许可证有效期内获取软件更新、安全补丁和功能增强
// | 4. 商业部署:将基于本软件开发的应用程序用于商业运营和生产环境
// +======================================================================
// | 【许可证获取】
// | 1. 获取途径:仅通过小蚂蚁云官方渠道购买(官网、授权代理商),购买记录凭证
// | 2. 转让限制:许可证与购买主体绑定,未经书面同意不得转让
// | 3. 终止情形:许可证到期、违反协议、破产清算等情况下自动终止
// +======================================================================
// | 【合法使用要求】
// | 1. 仅限用于合法合规的软件研发
// | 2. 严禁用于任何违法、侵权或恶意行为
// | 3. 严禁违反我国法律法规和政策
// +======================================================================
// | 【免责声明】
// | 因使用本框架导致的以下情况,我们概不负责:
// | 1. 项目研发中的意外、疏忽、违约等风险
// | 2. 侵犯第三方知识产权或其他合法权益
// | 3. 任何直接、间接、附带或衍生一切损失
// | 4. 用户违规违法行为的一切法律后果
// +======================================================================
// | 【协议重要提示】
// | 1. 本声明是《小蚂蚁云最终用户许可协议》的摘要版本
// | 2. 下载、安装或使用本软件即表示您接受全部协议条款
// | 3. 我们保留单方面修改本声明的权利,修改后的版本在官网公布即生效
// | 4. 用户有义务定期查看最新版本,继续使用视为接受修改
// +======================================================================
// | 【侵权举报与合规】
// | 1. 知识产权侵权举报:请联系官方客服
// | 2. 许可证合规咨询:xiaomayicloud@163.com
// | 3. 违规使用将终止授权并追究责任
// | 4. 无条件配合司法机关调查取证
// | 5. 保留一切法律追诉权利
// | 6. 对有效举报和合规建议,我们将给予感谢和奖励
// +======================================================================
package com.xiaomayi.datasource.context;
import lombok.extern.slf4j.Slf4j;
/**
* 数据源上下文持有器
* 使用ThreadLocal管理当前线程的数据源选择,实现线程安全的数据源切换
*
* 设计原理:
* 1. ThreadLocal为每个线程提供独立的变量副本,避免多线程环境下的数据竞争
* 2. 每个线程可以独立设置和获取自己的数据源,互不干扰
* 3. 使用后必须清理,防止内存泄漏和线程池复用导致的数据源污染
*
* 使用场景:
* - 在AOP切面中设置数据源
* - 在业务方法中手动切换数据源
* - 在事务管理中保持数据源一致性
*
* 线程安全说明:
* - 每个线程操作自己的ThreadLocal变量,天然线程安全
* - 无需额外的同步机制
*/
@Slf4j
public class DataSourceContextHolder {
/**
* 数据源上下文ThreadLocal
* 存储当前线程选择的数据源标识
*
* ThreadLocal特性:
* - 每个线程有独立的副本
* - 线程结束时自动清理(但线程池中线程会复用,需要手动清理)
* - 支持继承(InheritableThreadLocal),但这里不需要
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 备用数据源上下文(用于嵌套数据源切换)
* 当需要多层数据源切换时,保存上一级的数据源设置
*/
private static final ThreadLocal<String> BACKUP_CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 私有构造函数,防止实例化
* 这是一个工具类,所有方法都是静态的
*/
private DataSourceContextHolder() {
throw new UnsupportedOperationException("DataSourceContextHolder是工具类,不能实例化");
}
/**
* 设置当前线程的数据源
*
* @param dataSource 数据源标识,对应数据源配置中的key
*
* 使用示例:
* {@code
* DataSourceContextHolder.setDataSource("master");
* try {
* // 执行数据库操作
* } finally {
* DataSourceContextHolder.clearDataSource();
* }
* }
*
* 注意事项:
* 1. 必须在finally块中调用clearDataSource()清理,避免内存泄漏
* 2. 数据源标识必须与DataSourceConfig中配置的数据源key一致
* 3. 如果设置null,将使用默认数据源
*/
public static void setDataSource(String dataSource) {
if (dataSource == null) {
log.warn("尝试设置null数据源,这将使用默认数据源");
CONTEXT_HOLDER.remove();
} else {
String previousDataSource = getDataSource();
if (previousDataSource != null) {
// 如果已经有数据源设置,保存到备用上下文(支持嵌套切换)
BACKUP_CONTEXT_HOLDER.set(previousDataSource);
log.info("保存备用数据源: {}", previousDataSource);
}
CONTEXT_HOLDER.set(dataSource);
log.info("设置数据源: {}", dataSource);
}
}
/**
* 获取当前线程的数据源
*
* @return String 当前数据源标识,如果未设置返回null
*
* 返回值说明:
* - null: 表示使用默认数据源(主数据源)
* - 非null: 表示使用指定的数据源
*
* 使用场景:
* - DynamicDataSource.determineCurrentLookupKey()中调用
* - 调试和日志记录
* - 手动数据源管理
*/
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
/**
* 清除当前线程的数据源设置
* 恢复到默认数据源(主数据源)
*
* 重要:必须在try-finally块中调用,确保资源清理
*
* 清理逻辑:
* 1. 如果存在备用数据源,恢复到备用数据源(嵌套切换场景)
* 2. 否则完全清除数据源设置,使用默认数据源
*
* 内存泄漏风险:
* 如果不调用此方法,在线程池场景下会导致:
* - 数据源污染:线程复用时使用错误的数据源
* - 内存泄漏:ThreadLocal中的值不会被GC回收
*/
public static void clearDataSource() {
String currentDataSource = getDataSource();
if (BACKUP_CONTEXT_HOLDER.get() != null) {
// 存在备用数据源,恢复到备用数据源
String backupDataSource = BACKUP_CONTEXT_HOLDER.get();
CONTEXT_HOLDER.set(backupDataSource);
BACKUP_CONTEXT_HOLDER.remove();
log.info("恢复到备用数据源: {} (从 {} 恢复)", backupDataSource, currentDataSource);
} else {
// 没有备用数据源,完全清除
CONTEXT_HOLDER.remove();
log.info("清除数据源设置: {}", currentDataSource);
}
}
/**
* 强制清除所有数据源设置
* 同时清除主上下文和备用上下文
*
* 使用场景:
* - 请求处理完成后的最终清理
* - 异常情况下的资源回收
* - 测试环境下的状态重置
*/
public static void clearAll() {
CONTEXT_HOLDER.remove();
BACKUP_CONTEXT_HOLDER.remove();
log.info("强制清除所有数据源设置");
}
/**
* 检查当前线程是否设置了数据源
*
* @return boolean true-已设置数据源,false-使用默认数据源
*/
public static boolean hasDataSource() {
return CONTEXT_HOLDER.get() != null;
}
/**
* 获取当前数据源状态的详细描述
* 用于调试和监控
*
* @return String 数据源状态描述
*/
public static String getDataSourceStatus() {
String current = getDataSource();
String backup = BACKUP_CONTEXT_HOLDER.get();
StringBuilder status = new StringBuilder();
status.append("当前数据源: ").append(current != null ? current : "默认(主库)");
if (backup != null) {
status.append(", 备用数据源: ").append(backup);
}
return status.toString();
}
/**
* 获取当前线程的ID和数据源信息
* 用于多线程调试
*
* @return String 线程和数据源信息
*/
public static String getThreadDataSourceInfo() {
return String.format("Thread[%d] - %s",
Thread.currentThread().getId(),
getDataSourceStatus());
}
}
@@ -0,0 +1,275 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | Copyright (c) 2020-2026 小蚂蚁云团队 版权所有
// +----------------------------------------------------------------------
// | 官方平台: https://www.xiaomayicloud.com
// +======================================================================
// | 【知识产权声明】
// | 1. 权属声明:本软件是受《中华人民共和国著作权法》
// | 保护的专有财产,完整版权归小蚂蚁云团队所有
// | 2. 权利范围:所有知识产权(商标、专利、著作权、
// | 商业秘密等)均归小蚂蚁云团队所有,受法律保护
// | 3. 授权性质:用户仅可获得有限的、非独占的使用许可
// | 4. 禁止行为:未经书面授权,禁止对本软件本身
// | 申请任何知识产权
// | 5. 权利保留:未在本协议中明示授予的一切权利
// | 均由小蚂蚁云团队保留
// +======================================================================
// | 【授权状态说明】
// | 1. 未授权状态:用户未购买有效的商业许可证
// | 2. 商业授权:已购买正式商业许可证的用户
// +======================================================================
// | 【绝对禁止条款】
// | 无论授权状态如何,任何用户均严格禁止以下行为:
// | 1. 逆向工程:对软件进行反编译、反汇编、技术破解或任何形式的逆向分析
// | 2. 版权破坏:移除、修改、遮挡软件中的版权标识、许可控制或技术保护措施
// | 3. 非法传播:复制、分发、出租、出借、转售、再许可本软件或其许可权利
// | 4. 竞争性使用:利用本软件的代码、架构、设计开发直接竞争的产品或服务
// | 5. 侵权使用:将软件用于侵犯第三方知识产权或其他合法权益的用途
// +======================================================================
// | 【商业授权许可范围】
// | 购买有效商业许可证的用户,在遵守上述禁止条款的前提下,获得:
// | 1. 使用权利:在许可证约定的项目数量、开发者数量范围内使用本软件
// | 2. 技术支持:商业授权用户可以获取本地开发部署的技术支持服务
// | 3. 版本更新:在许可证有效期内获取软件更新、安全补丁和功能增强
// | 4. 商业部署:将基于本软件开发的应用程序用于商业运营和生产环境
// +======================================================================
// | 【许可证获取】
// | 1. 获取途径:仅通过小蚂蚁云官方渠道购买(官网、授权代理商),购买记录凭证
// | 2. 转让限制:许可证与购买主体绑定,未经书面同意不得转让
// | 3. 终止情形:许可证到期、违反协议、破产清算等情况下自动终止
// +======================================================================
// | 【合法使用要求】
// | 1. 仅限用于合法合规的软件研发
// | 2. 严禁用于任何违法、侵权或恶意行为
// | 3. 严禁违反我国法律法规和政策
// +======================================================================
// | 【免责声明】
// | 因使用本框架导致的以下情况,我们概不负责:
// | 1. 项目研发中的意外、疏忽、违约等风险
// | 2. 侵犯第三方知识产权或其他合法权益
// | 3. 任何直接、间接、附带或衍生一切损失
// | 4. 用户违规违法行为的一切法律后果
// +======================================================================
// | 【协议重要提示】
// | 1. 本声明是《小蚂蚁云最终用户许可协议》的摘要版本
// | 2. 下载、安装或使用本软件即表示您接受全部协议条款
// | 3. 我们保留单方面修改本声明的权利,修改后的版本在官网公布即生效
// | 4. 用户有义务定期查看最新版本,继续使用视为接受修改
// +======================================================================
// | 【侵权举报与合规】
// | 1. 知识产权侵权举报:请联系官方客服
// | 2. 许可证合规咨询:xiaomayicloud@163.com
// | 3. 违规使用将终止授权并追究责任
// | 4. 无条件配合司法机关调查取证
// | 5. 保留一切法律追诉权利
// | 6. 对有效举报和合规建议,我们将给予感谢和奖励
// +======================================================================
package com.xiaomayi.datasource.context;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 动态数据源路由类
* 继承Spring的AbstractRoutingDataSource,实现运行时数据源动态切换
* <p>
* 核心功能:
* 1. 管理多个数据源的映射关系
* 2. 根据当前线程上下文路由到正确的数据源
* 3. 提供数据源查找和切换的基础设施
* <p>
* 工作流程:
* 1. 应用启动时,加载所有配置的数据源到targetDataSources
* 2. 每次数据库操作时,调用determineCurrentLookupKey()获取数据源key
* 3. 根据key从targetDataSources中获取对应的DataSource实例
* 4. 使用获取的DataSource执行数据库操作
* <p>
* 设计模式:策略模式 + 模板方法模式
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 默认数据源(主数据源)
* 当没有明确指定数据源时使用
* -- GETTER --
* 获取默认数据源
*
* @return DataSource 默认数据源实例
*/
@Getter
private final DataSource defaultDataSource;
/**
* 目标数据源映射
* key: 数据源标识(如:MASTER, SLAVE1, SLAVE2等)
* value: 对应的DataSource实例
*/
private final Map<Object, Object> targetDataSources;
/**
* 构造函数
*
* @param defaultTargetDataSource 默认目标数据源(主数据源)
* @param targetDataSources 所有可用的数据源映射
* <p>
* 参数说明:
* - defaultTargetDataSource: 当determineCurrentLookupKey()返回null时使用
* - targetDataSources: 所有已配置的数据源,包括主数据源和从数据源
* <p>
* 注意:defaultTargetDataSource通常也包含在targetDataSources中
*/
public DynamicDataSource(DataSource defaultTargetDataSource,
Map<Object, Object> targetDataSources) {
// 保存参数用于日志和监控
this.defaultDataSource = defaultTargetDataSource;
this.targetDataSources = targetDataSources;
// 调用父类设置方法
// 设置默认数据源(当无法确定数据源时使用)
super.setDefaultTargetDataSource(defaultTargetDataSource);
// 设置目标数据源映射(所有可用的数据源)
super.setTargetDataSources(targetDataSources);
// 初始化数据源,这个方法会:
// 1. 验证数据源配置
// 2. 解析resolvedDataSources和resolvedDefaultDataSource
// 3. 准备数据源路由
super.afterPropertiesSet();
log.info("动态数据源初始化完成,默认数据源: {}, 可用数据源数量: {}",
getDataSourceName(defaultTargetDataSource),
targetDataSources.size());
}
/**
* 决定当前查找键(数据源标识)
* 这是AbstractRoutingDataSource的核心抽象方法,每次数据库操作时都会被调用
*
* @return Object 数据源标识,对应targetDataSources中的key
* <p>
* 执行时机:
* - 每次数据库操作前(getConnection()时)
* - 事务开始时
* - 连接获取时
* <p>
* 返回值处理:
* - 非null: 从targetDataSources中查找对应的DataSource
* - null: 使用defaultTargetDataSource
* <p>
* 异常处理:
* - 如果返回的key在targetDataSources中不存在,抛出IllegalArgumentException
*/
@Override
protected Object determineCurrentLookupKey() {
// 从DataSourceContextHolder获取当前线程的数据源设置
String dataSourceKey = DataSourceContextHolder.getDataSource();
if (dataSourceKey != null) {
// 验证数据源是否存在
if (!targetDataSources.containsKey(dataSourceKey)) {
log.info("数据源 '{}' 不存在,使用默认数据源。可用数据源: {}",
dataSourceKey, targetDataSources.keySet());
return null;
}
log.info("路由到数据源: {}", dataSourceKey);
return dataSourceKey;
}
// 没有设置数据源,使用默认数据源(主库)
log.info("使用默认数据源");
return null;
}
/**
* 获取所有目标数据源映射
*
* @return Map<Object, Object> 数据源映射的不可修改视图
*/
public Map<Object, Object> getTargetDataSources() {
return Map.copyOf(targetDataSources);
}
/**
* 根据数据源标识获取数据源实例
*
* @param dataSourceKey 数据源标识
* @return DataSource 数据源实例,如果不存在返回null
*/
public DataSource getDataSourceByKey(String dataSourceKey) {
Object dataSource = targetDataSources.get(dataSourceKey);
return dataSource instanceof DataSource ? (DataSource) dataSource : null;
}
/**
* 检查数据源是否存在
*
* @param dataSourceKey 数据源标识
* @return boolean true-存在,false-不存在
*/
public boolean containsDataSource(String dataSourceKey) {
return targetDataSources.containsKey(dataSourceKey);
}
/**
* 获取所有可用的数据源标识
*
* @return Set<Object> 数据源标识集合
*/
public java.util.Set<Object> getAvailableDataSourceKeys() {
return targetDataSources.keySet();
}
/**
* 获取数据源名称(用于日志输出)
*
* @param dataSource 数据源实例
* @return String 数据源描述
*/
private String getDataSourceName(DataSource dataSource) {
if (dataSource == null) {
return "null";
}
// 尝试获取有意义的名称
if (dataSource instanceof org.springframework.jdbc.datasource.SimpleDriverDataSource) {
return "SimpleDriverDataSource";
} else if (dataSource instanceof com.alibaba.druid.pool.DruidDataSource) {
return "DruidDataSource";
} else {
return dataSource.getClass().getSimpleName();
}
}
/**
* 数据源状态信息
* 用于监控和调试
*
* @return String 数据源状态描述
*/
public String getStatusInfo() {
return String.format("DynamicDataSource[默认数据源: %s, 可用数据源: %s, 当前线程数据源: %s]",
getDataSourceName(defaultDataSource),
targetDataSources.keySet(),
DataSourceContextHolder.getDataSourceStatus());
}
/**
* 销毁方法
* 清理资源,防止内存泄漏
*/
public void destroy() {
log.info("开始销毁动态数据源...");
DataSourceContextHolder.clearAll();
log.info("动态数据源销毁完成");
}
}