签入版本

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,548 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | 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.aspect;
import com.xiaomayi.datasource.annotation.DataSource;
import com.xiaomayi.datasource.context.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 数据源切面处理类 - 完整版
* 使用Spring AOP实现基于注解的数据源动态切换
* 提供完整的切面生命周期方法:前置通知、后置通知、返回通知、异常通知、环绕通知
* <p>
* 核心功能:
* 1. 拦截带有@DataSource注解的方法或类
* 2. 在方法执行前根据注解值切换数据源
* 3. 在方法执行后恢复原始数据源设置
* 4. 提供完整的切面生命周期监控
* 5. 保证数据源切换的线程安全和资源清理
* <p>
* 切面执行顺序:
* 1. doBefore() - 前置通知
* 2. around() - 环绕通知(包含实际的方法执行)
* 3. doAfterReturning()- 返回通知(方法正常返回)
* 或 doAfterThrowing()- 异常通知(方法抛出异常)
* 4. doAfter() - 后置通知(最终通知)
*
* @author 小蚂蚁云团队
* @since 2024-05-25
*/
@Aspect
@Component
@Order(1) // 设置切面执行顺序,确保在事务切面之前执行
@Slf4j
public class DataSourceAspect {
/**
* 数据源切点定义
* 使用AspectJ切点表达式定义拦截规则
* <p>
* 切点表达式说明:
* - @annotation: 拦截带有指定注解的方法
* - @within: 拦截带有指定注解的类中的所有方法
* <p>
* 拦截范围:
* 1. 任何带有@DataSource注解的方法
* 2. 任何在带有@DataSource注解的类中的方法
*/
@Pointcut("@annotation(com.xiaomayi.datasource.annotation.DataSource) || " +
"@within(com.xiaomayi.datasource.annotation.DataSource)")
public void dataSourcePointCut() {
// 切点方法体为空,仅用于定义切点表达式
}
/**
* 前置通知 - 在目标方法执行前执行
* 主要用于数据源的初始设置和验证
*
* @param point 连接点对象
* <p>
* 执行时机:
* - 在目标方法执行之前
* - 在环绕通知的proceed()调用之前
* <p>
* 主要功能:
* 1. 验证数据源注解配置
* 2. 记录方法开始执行的日志
* 3. 准备数据源切换的环境
*/
@Before("dataSourcePointCut()")
public void doBefore(JoinPoint point) {
// 获取当前线程ID,用于日志追踪
long threadId = Thread.currentThread().getId();
// 解析数据源注解配置
DataSource dataSourceAnnotation = getDataSource(point);
if (dataSourceAnnotation != null) {
String targetDataSourceKey = dataSourceAnnotation.value().name();
String originalDataSource = DataSourceContextHolder.getDataSource();
log.debug("线程[{}] 前置通知 - 准备数据源切换: {} -> {}, 目标方法: {}.{}",
threadId,
originalDataSource != null ? originalDataSource : "默认(主库)",
targetDataSourceKey,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
// 验证数据源是否存在(可选,也可以在切换时验证)
validateDataSource(targetDataSourceKey, point);
} else {
log.info("线程[{}] 前置通知 - 未找到数据源注解,使用默认数据源执行方法: {}.{}",
threadId,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
}
}
/**
* 后置通知 - 在目标方法执行后执行(无论是否抛出异常)
* 主要用于资源清理和状态恢复的最终保证
*
* @param point 连接点对象
* <p>
* 执行时机:
* - 在目标方法执行完成后(无论正常返回还是抛出异常)
* - 在返回通知或异常通知之后
* - 在环绕通知的finally块之后
* <p>
* 主要功能:
* 1. 确保数据源状态最终被清理
* 2. 记录方法执行的最终状态
* 3. 提供额外的安全保障,防止数据源泄漏
*/
@After("dataSourcePointCut()")
public void doAfter(JoinPoint point) {
long threadId = Thread.currentThread().getId();
log.debug("线程[{}] 后置通知 - 方法执行完成,当前数据源状态: {}, 方法: {}.{}",
threadId,
DataSourceContextHolder.getDataSourceStatus(),
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
// 额外的安全检查:如果发现异常的数据源状态,进行强制清理
performSafetyCheck();
}
/**
* 返回通知 - 在目标方法正常返回后执行
* 主要用于记录成功的操作和性能监控
*
* @param point 连接点对象
* @param result 目标方法的返回值
* <p>
* 执行时机:
* - 仅在目标方法正常返回时执行
* - 在目标方法执行完成后,后置通知之前
* <p>
* 主要功能:
* 1. 记录方法成功执行的日志
* 2. 监控方法执行性能
* 3. 处理返回值相关的逻辑(如记录操作结果)
*/
@AfterReturning(pointcut = "dataSourcePointCut()", returning = "result")
public void doAfterReturning(JoinPoint point, Object result) {
long threadId = Thread.currentThread().getId();
// 记录方法成功执行的信息
String returnInfo = getReturnValueInfo(result);
log.debug("线程[{}] 返回通知 - 方法执行成功, 返回值: {}, 方法: {}.{}, 当前数据源: {}",
threadId,
returnInfo,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName(),
DataSourceContextHolder.getDataSourceStatus());
// 可以在这里添加成功执行的业务逻辑,如:
// - 记录操作日志
// - 更新监控指标
// - 发送成功通知
}
/**
* 异常通知 - 在目标方法抛出异常后执行
* 主要用于异常处理和数据源状态的异常恢复
*
* @param point 连接点对象
* @param ex 目标方法抛出的异常
* <p>
* 执行时机:
* - 仅在目标方法抛出异常时执行
* - 在目标方法执行完成后,后置通知之前
* <p>
* 主要功能:
* 1. 记录方法执行失败的日志
* 2. 处理异常情况下的数据源状态
* 3. 提供异常恢复机制
* 4. 发送异常告警
*/
@AfterThrowing(pointcut = "dataSourcePointCut()", throwing = "ex")
public void doAfterThrowing(JoinPoint point, Throwable ex) {
long threadId = Thread.currentThread().getId();
log.error("线程[{}] 异常通知 - 方法执行失败, 异常: {}, 方法: {}.{}, 当前数据源: {}",
threadId,
ex.getMessage(),
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName(),
DataSourceContextHolder.getDataSourceStatus(),
ex);
// 异常处理逻辑
handleDataSourceException(ex, point);
// 可以在这里添加异常处理业务逻辑,如:
// - 发送异常告警
// - 记录错误日志到数据库
// - 执行异常恢复操作
}
/**
* 环绕通知 - 在目标方法执行前后执行
* 这是最强大的通知类型,可以控制目标方法的执行
*
* @param point 连接点对象
* @return Object 目标方法的执行结果
* @throws Throwable 目标方法抛出的任何异常
* <p>
* 执行时机:
* - 包含目标方法的整个执行过程
* - 可以控制是否执行目标方法,以及何时执行
* <p>
* 主要功能:
* 1. 实际的数据源切换逻辑
* 2. 方法执行时间的监控
* 3. 异常处理和资源清理
*/
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long threadId = Thread.currentThread().getId();
long startTime = System.currentTimeMillis();
// 解析数据源注解配置
DataSource dataSourceAnnotation = getDataSource(point);
if (dataSourceAnnotation == null) {
// 没有找到注解,直接执行原方法
log.info("线程[{}] 环绕通知 - 未找到数据源注解,直接执行方法: {}.{}",
threadId,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
return point.proceed();
}
String targetDataSourceKey = dataSourceAnnotation.value().name();
String originalDataSource = DataSourceContextHolder.getDataSource();
Object result = null;
boolean success = false;
log.info("线程[{}] 环绕通知 - 开始数据源切换: {} -> {}, 方法: {}.{}",
threadId,
originalDataSource != null ? originalDataSource : "默认(主库)",
targetDataSourceKey,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
try {
// 设置新的数据源
DataSourceContextHolder.setDataSource(targetDataSourceKey);
// 执行目标方法
result = point.proceed();
success = true;
return result;
} catch (Throwable ex) {
// 记录异常信息,但不需要在这里处理,异常通知会处理
success = false;
throw ex;
} finally {
// 计算执行时间
long executionTime = System.currentTimeMillis() - startTime;
// 恢复数据源设置
DataSourceContextHolder.clearDataSource();
// 记录执行结果
log.info("线程[{}] 环绕通知 - 方法执行{},耗时: {}ms, 方法: {}.{}, 数据源状态: {}",
threadId,
success ? "成功" : "失败",
executionTime,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName(),
DataSourceContextHolder.getDataSourceStatus());
}
}
/**
* 验证数据源是否存在
*
* @param dataSourceKey 数据源标识
* @param point 连接点对象
*/
private void validateDataSource(String dataSourceKey, JoinPoint point) {
try {
// 这里可以添加数据源验证逻辑
// 例如:检查数据源是否在配置中启用
// 由于数据源验证主要在DynamicDataSource中完成,这里主要做日志记录
log.info("数据源验证 - 数据源: {} 准备用于方法: {}.{}",
dataSourceKey,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName());
} catch (Exception e) {
log.warn("数据源验证失败 - 数据源: {}, 方法: {}.{}, 错误: {}",
dataSourceKey,
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName(),
e.getMessage());
}
}
/**
* 执行安全检查
* 防止数据源状态异常导致的内存泄漏或数据污染
*/
private void performSafetyCheck() {
try {
// 检查当前线程的数据源状态
String currentDataSource = DataSourceContextHolder.getDataSource();
// 如果发现异常的数据源状态,进行强制清理
if (currentDataSource != null && currentDataSource.contains("ERROR")) {
log.warn("安全检查 - 检测到异常数据源状态: {}, 进行强制清理", currentDataSource);
DataSourceContextHolder.clearAll();
}
// 可以添加更多的安全检查逻辑
// 例如:检查线程是否长时间持有数据源
} catch (Exception e) {
log.error("安全检查执行失败", e);
}
}
/**
* 处理数据源相关异常
*
* @param ex 异常对象
* @param point 连接点对象
*/
private void handleDataSourceException(Throwable ex, JoinPoint point) {
try {
// 记录异常相关的数据源信息
String dataSourceStatus = DataSourceContextHolder.getDataSourceStatus();
// 如果是数据源连接异常,可以尝试恢复或告警
if (isDataSourceConnectionException(ex)) {
log.error("数据源连接异常 - 方法: {}.{}, 数据源状态: {}, 异常: {}",
point.getSignature().getDeclaringType().getSimpleName(),
point.getSignature().getName(),
dataSourceStatus,
ex.getMessage());
// 可以在这里添加连接异常的处理逻辑
// 例如:发送告警、记录到监控系统等
}
// 确保在异常情况下数据源状态被正确清理
DataSourceContextHolder.clearAll();
} catch (Exception handlerEx) {
log.error("处理数据源异常时发生错误", handlerEx);
}
}
/**
* 判断是否是数据源连接异常
*
* @param ex 异常对象
* @return boolean true-是数据源连接异常,false-不是
*/
private boolean isDataSourceConnectionException(Throwable ex) {
if (ex == null) {
return false;
}
String message = ex.getMessage();
if (message == null) {
return false;
}
// 根据异常信息判断是否是数据源连接问题
return message.contains("Connection") ||
message.contains("DataSource") ||
message.contains("SQL") ||
message.contains("JDBC") ||
ex.getClass().getName().contains("SQL");
}
/**
* 获取返回值信息(用于日志记录)
*
* @param result 方法返回值
* @return String 返回值描述信息
*/
private String getReturnValueInfo(Object result) {
if (result == null) {
return "null";
}
try {
if (result instanceof Iterable) {
// 如果是集合类型,返回元素数量
int size = 0;
for (Object item : (Iterable<?>) result) {
size++;
if (size > 10) break; // 防止大集合的遍历
}
return String.format("Collection[%d elements]", size);
} else if (result.getClass().isArray()) {
// 如果是数组,返回长度
return String.format("Array[%d]", java.lang.reflect.Array.getLength(result));
} else {
// 其他类型返回简单类名
return result.getClass().getSimpleName();
}
} catch (Exception e) {
return "Error getting return info: " + e.getMessage();
}
}
/**
* 获取数据源注解配置
* 按照优先级查找@DataSource注解:方法级别 > 类级别
*
* @param point 连接点对象
* @return DataSource 数据源注解实例,如果未找到返回null
*/
private DataSource getDataSource(JoinPoint point) {
if (!(point.getSignature() instanceof MethodSignature)) {
return null;
}
MethodSignature signature = (MethodSignature) point.getSignature();
// 查找方法级别的@DataSource注解
DataSource methodAnnotation = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (Objects.nonNull(methodAnnotation)) {
return methodAnnotation;
}
// 查找类级别的@DataSource注解
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
/**
* 获取切面状态信息(用于监控和调试)
*
* @return String 切面状态描述
*/
public String getAspectStatus() {
return String.format("DataSourceAspect[激活状态: 已启用, 切面顺序: %d, 通知类型: 完整, 当前线程数据源: %s]",
1,
DataSourceContextHolder.getDataSourceStatus());
}
/**
* 检查切面是否正常工作(用于健康检查)
*
* @return boolean true-正常,false-异常
*/
public boolean isAspectHealthy() {
try {
// 测试基本的切面功能
DataSourceContextHolder.getDataSource();
return true;
} catch (Exception e) {
log.error("数据源切面健康检查失败", e);
return false;
}
}
}