签入版本

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,677 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | 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.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.support.jakarta.StatViewServlet;
import com.alibaba.druid.support.jakarta.WebStatFilter;
import com.xiaomayi.datasource.context.DynamicDataSource;
import com.xiaomayi.datasource.enums.DataSourceType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 数据源配置类 - 修复多个@Primary Bean冲突问题
* <p>
* 问题背景:
* 之前的配置中存在多个被标记为@Primary的DataSource Bean,导致Spring无法确定注入哪个Bean:
* - masterDataSource (标记了@Primary)
* - slave1DataSource (标记了@Primary)
* - dataSource (标记了@Primary)
* <p>
* Spring抛出异常:No qualifying bean of type 'javax.sql.DataSource' available:
* more than one 'primary' bean found among candidates
* <p>
* 解决方案:
* 1. 只保留动态数据源dataSource为@Primary,作为系统的主要数据源
* 2. 移除其他单个数据源Bean的@Primary注解
* 3. 使用@Qualifier明确标识各个数据源Bean的名称
* 4. 确保整个Spring容器中只有一个主要的DataSource Bean
* <p>
* 设计原则:
* - 单一主要原则:只有一个DataSource Bean可以标记为@Primary
* - 明确标识原则:使用@Qualifier明确指定Bean名称
* - 依赖清晰原则:通过构造函数明确依赖关系
*
* @author 小蚂蚁云团队
* @since 2024-05-25
*/
@Slf4j
@Configuration
public class DataSourceConfig {
/**
* 数据源映射缓存
* <p>
* 用途:
* - 存储所有已创建的数据源实例,便于统一管理
* - 提供数据源状态监控和调试信息
* - 支持动态数据源的路由和切换
* <p>
* 数据结构:
* key: 数据源名称(DataSourceType枚举的name,如:"MASTER", "SLAVE1")
* value: 对应的DataSource实例
* <p>
* 线程安全:
* 使用ConcurrentHashMap保证多线程环境下的安全访问
*/
private final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();
/**
* Spring环境配置对象
* <p>
* 用途:
* - 读取application.yml中的配置属性
* - 获取Druid监控相关的配置信息
* - 避免使用SpringContextHolder可能出现的初始化问题
* <p>
* 注入方式:
* 通过构造函数注入,确保依赖在对象创建时就已经可用
* 这种方式比字段注入更安全,支持不可变对象和更好的测试性
*/
private final Environment environment;
/**
* 构造函数 - 依赖注入
* <p>
* 设计优势:
* 1. 不可变性:environment字段被声明为final,确保线程安全
* 2. 明确的依赖:所有必需的依赖都在构造时明确指定
* 3. 易于测试:可以通过构造函数传入mock对象进行单元测试
* 4. 避免空指针:确保environment在对象使用时已经初始化
* 5. 循环依赖检测:Spring能更早地发现和报告循环依赖问题
*
* @param environment Spring环境配置对象,由Spring容器自动注入
*/
public DataSourceConfig(Environment environment) {
this.environment = environment;
log.info("DataSourceConfig初始化完成 - 使用构造函数注入Environment");
}
/**
* 主数据源配置Bean
* <p>
* 重要变更:移除了@Primary注解
* <p>
* 原因分析:
* 在之前的配置中,masterDataSource标记了@Primary,这会导致:
* 1. 当其他组件使用@Autowired DataSource dataSource时,Spring会注入masterDataSource
* 2. 但系统中还有dataSource(动态数据源)也标记了@Primary
* 3. Spring无法确定哪个是真正的主要数据源,抛出异常
* <p>
* 解决方案:
* 移除单个数据源Bean的@Primary注解,只保留动态数据源的@Primary
* <p>
* 配置绑定:
* 使用@ConfigurationProperties将配置文件中的属性绑定到数据源实例
* 前缀:spring.datasource.dynamic.datasource.master
*
* @return DruidDataSource 主数据源实例
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DataSource masterDataSource() {
log.info("初始化主数据源...");
// 使用DruidDataSourceBuilder创建数据源,自动应用配置属性
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
// 将数据源添加到缓存映射中,便于统一管理
dataSourceMap.put(DataSourceType.MASTER.name(), dataSource);
log.info("主数据源初始化完成");
return dataSource;
}
/**
* 从数据源1配置Bean
* <p>
* 注解说明:
* - @Bean: 声明为Spring Bean,方法名作为默认的Bean名称
* - @Qualifier("slave1DataSource"): 明确指定Bean的名称为"slave1DataSource"
* - @ConfigurationProperties: 绑定配置属性,前缀为spring.datasource.dynamic.datasource.slave1
* - @ConditionalOnProperty: 条件化创建Bean,只有当slave1.enabled=true时才创建
* <p>
* 条件化创建:
* 这个Bean只有在配置文件中spring.datasource.dynamic.datasource.slave1.enabled=true时才会创建
* 如果enabled=false或未配置,Spring不会创建这个Bean,注入时会得到null
*
* @return DruidDataSource 从数据源1实例,如果未启用则返回null
*/
@Bean
@Qualifier("slave1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave1")
@ConditionalOnProperty(prefix = "spring.datasource.dynamic.datasource.slave1", name = "enabled", havingValue = "true")
public DataSource slave1DataSource() {
log.info("初始化从数据源1...");
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSourceMap.put(DataSourceType.SLAVE1.name(), dataSource);
log.info("从数据源1初始化完成");
return dataSource;
}
/**
* 从数据源2配置Bean
* <p>
* 配置方式与从数据源1相同,使用不同的配置前缀和Bean名称
*
* @return DruidDataSource 从数据源2实例,如果未启用则返回null
*/
@Bean
@Qualifier("slave2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave2")
@ConditionalOnProperty(prefix = "spring.datasource.dynamic.datasource.slave2", name = "enabled", havingValue = "true")
public DataSource slave2DataSource() {
log.info("初始化从数据源2...");
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSourceMap.put(DataSourceType.SLAVE2.name(), dataSource);
log.info("从数据源2初始化完成");
return dataSource;
}
/**
* PostgreSQL数据源配置Bean
* <p>
* 用于连接PostgreSQL数据库,配置方式与其他数据源类似
* 使用PostgreSQL特定的JDBC驱动和连接URL格式
*
* @return DruidDataSource PostgreSQL数据源实例,如果未启用则返回null
*/
@Bean
@Qualifier("postgresDataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.postgres")
@ConditionalOnProperty(prefix = "spring.datasource.dynamic.datasource.postgres", name = "enabled", havingValue = "true")
public DataSource postgresDataSource() {
log.info("初始化PostgreSQL数据源...");
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSourceMap.put(DataSourceType.POSTGRESQL.name(), dataSource);
log.info("PostgreSQL数据源初始化完成");
return dataSource;
}
/**
* SQL Server数据源配置Bean
* <p>
* 用于连接SQL Server数据库,配置方式与其他数据源类似
* 使用SQL Server特定的JDBC驱动和连接URL格式
*
* @return DruidDataSource SQL Server数据源实例,如果未启用则返回null
*/
@Bean
@Qualifier("sqlServerDataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.sqlserver")
@ConditionalOnProperty(prefix = "spring.datasource.dynamic.datasource.sqlserver", name = "enabled", havingValue = "true")
public DataSource sqlServerDataSource() {
log.info("初始化SQL Server数据源...");
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSourceMap.put(DataSourceType.SQLSERVER.name(), dataSource);
log.info("SQL Server数据源初始化完成");
return dataSource;
}
/**
* 动态数据源配置Bean - 系统中唯一标记为@Primary的DataSource
* <p>
* 核心功能:
* 将所有单个数据源组合成动态数据源,支持运行时数据源切换
* <p>
* 重要设计:
* 这是整个Spring容器中唯一标记为@Primary的DataSource Bean
* 当其他组件使用@Autowired DataSource dataSource时,Spring会自动注入这个Bean
* <p>
* 参数注入:
* 使用@Qualifier明确指定要注入的Bean名称,解决多个同类型Bean的冲突
* Spring会根据@Qualifier的值找到对应的Bean进行注入
* <p>
* 执行流程:
* 1. 收集所有已启用的数据源(不为null的数据源)
* 2. 创建目标数据源映射(Map<数据源名称, DataSource实例>)
* 3. 创建DynamicDataSource实例,设置主数据源为默认数据源
* 4. 返回配置完成的动态数据源
*
* @param masterDataSource 主数据源(必须存在)
* @param slave1DataSource 从数据源1(可能为null,如果未启用)
* @param slave2DataSource 从数据源2(可能为null,如果未启用)
* @param postgresDataSource PostgreSQL数据源(可能为null,如果未启用)
* @param sqlServerDataSource SQL Server数据源(可能为null,如果未启用)
* @return DynamicDataSource 动态数据源实例,标记为@Primary
*/
@Bean
@Primary // 重要:这是系统中唯一标记为@Primary的DataSource Bean
public DynamicDataSource dataSource(
// 主数据源必须存在
@Qualifier("masterDataSource") DataSource masterDataSource,
// 其他数据源都是可选的,允许为null
@Autowired(required = false) @Qualifier("slave1DataSource") DataSource slave1DataSource,
@Autowired(required = false) @Qualifier("slave2DataSource") DataSource slave2DataSource,
@Autowired(required = false) @Qualifier("postgresDataSource") DataSource postgresDataSource,
@Autowired(required = false) @Qualifier("sqlServerDataSource") DataSource sqlServerDataSource) {
log.info("开始配置动态数据源(主要数据源)...");
// 创建目标数据源映射,用于存储所有可用的数据源
Map<Object, Object> targetDataSources = new HashMap<>();
// 添加主数据源(必须存在)
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
log.info("添加主数据源: {}", DataSourceType.MASTER.name());
// 按条件添加其他数据源,如果数据源为null(未启用),则跳过
addDataSourceIfEnabled(targetDataSources, DataSourceType.SLAVE1.name(), slave1DataSource);
addDataSourceIfEnabled(targetDataSources, DataSourceType.SLAVE2.name(), slave2DataSource);
addDataSourceIfEnabled(targetDataSources, DataSourceType.POSTGRESQL.name(), postgresDataSource);
addDataSourceIfEnabled(targetDataSources, DataSourceType.SQLSERVER.name(), sqlServerDataSource);
log.info("动态数据源初始化完成,已加载数据源数量: {}", targetDataSources.size());
log.info("已加载数据源: {}", targetDataSources.keySet());
// 验证至少有一个数据源可用
if (targetDataSources.isEmpty()) {
throw new IllegalStateException("动态数据源配置错误:没有可用的数据源");
}
// 创建动态数据源实例
// 参数1: 默认数据源(当没有明确指定数据源时使用)
// 参数2: 所有可用的目标数据源映射
DynamicDataSource dynamicDataSource = new DynamicDataSource(masterDataSource, targetDataSources);
log.info("动态数据源配置成功,已设置为主要数据源(@Primary)");
return dynamicDataSource;
}
/**
* 条件添加数据源辅助方法
* <p>
* 功能:安全地将数据源添加到目标映射中,如果数据源为null则跳过
* <p>
* 设计考虑:
* - 避免在每个数据源添加时重复进行null检查
* - 提供统一的日志记录格式
* - 提高代码的可读性和可维护性
* - 支持条件化数据源加载
*
* @param targetDataSources 目标数据源映射,用于存储所有可用的数据源
* @param dataSourceName 数据源名称(使用DataSourceType枚举的name)
* @param dataSource 数据源实例,可能为null(如果对应的Bean未创建)
*/
private void addDataSourceIfEnabled(Map<Object, Object> targetDataSources,
String dataSourceName,
DataSource dataSource) {
if (dataSource != null) {
// 检查是否已经存在相同名称的数据源
if (targetDataSources.containsKey(dataSourceName)) {
log.warn("数据源 '{}' 已存在,跳过重复添加", dataSourceName);
return;
}
// 验证数据源是否有效
if (isDataSourceValid(dataSource)) {
targetDataSources.put(dataSourceName, dataSource);
log.info("数据源 '{}' 已加载", dataSourceName);
} else {
log.warn("数据源 '{}' 无效,跳过加载", dataSourceName);
}
} else {
log.debug("数据源 '{}' 未启用,跳过加载", dataSourceName);
}
}
/**
* 验证数据源有效性
* <p>
* 检查数据源的基本可用性,避免添加无效的数据源
*/
private boolean isDataSourceValid(DataSource dataSource) {
try {
// 简单的有效性检查:尝试获取连接
if (dataSource.getConnection() != null) {
return true;
}
} catch (Exception e) {
log.warn("数据源连接测试失败: {}", e.getMessage());
}
return false;
}
/**
* 事务管理器配置Bean
* <p>
* 功能:配置Spring的事务管理器,用于管理数据库事务
* <p>
* 注解说明:
* - @Primary: 标记为主要事务管理器,当有多个事务管理器时优先使用
* - @Qualifier: 明确指定要使用的数据源Bean名称为"dataSource"
* <p>
* 事务管理:
* - 支持声明式事务(使用@Transactional注解)
* - 支持编程式事务(使用TransactionTemplate)
* - 自动参与Spring的事务同步
* - 支持事务传播行为和隔离级别配置
* <p>
* 注意:事务管理器可以标记为@Primary,因为它与DataSource类型不同
* 不会与DataSource Bean产生@Primary冲突
*
* @param dataSource 动态数据源实例,由Spring根据@Qualifier注入
* @return DataSourceTransactionManager 事务管理器实例
*/
@Bean
@Primary
public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DynamicDataSource dataSource) {
log.info("配置动态数据源事务管理器...");
// 创建事务管理器,使用动态数据源
// 这样事务管理器就能支持跨数据源的事务管理
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
log.info("动态数据源事务管理器配置完成");
return transactionManager;
}
/**
* Druid 监控 Servlet 配置Bean
* <p>
* 功能:配置Druid内置的监控界面,用于监控数据库连接池状态
* <p>
* 注解说明:
* - @ConditionalOnProperty: 条件化创建,只有当监控功能启用时才创建
* matchIfMissing = true: 如果配置项不存在,默认创建此Bean
* <p>
* 监控功能:
* - SQL监控:查看执行的SQL语句、执行时间、影响行数等
* - 数据源监控:查看连接池的活跃连接、空闲连接、等待连接等
* - Web应用监控:查看URI请求统计、Session监控等
* - 安全控制:支持IP白名单、登录认证等
* <p>
* 访问地址:http://localhost:8080/druid/
* 默认登录:admin / 123456(可在配置文件中修改)
*
* @return ServletRegistrationBean<StatViewServlet> Druid监控Servlet注册Bean
*/
@Bean
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true)
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
log.info("初始化Druid监控Servlet...");
// 创建Servlet注册Bean
// 参数1: Druid的StatViewServlet,提供监控界面
// 参数2: 访问路径映射,所有以/druid/开头的请求都会由这个Servlet处理
ServletRegistrationBean<StatViewServlet> servletRegistrationBean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
// 从环境配置中获取监控页面登录用户名,默认值为"admin"
String loginUsername = environment.getProperty("spring.datasource.druid.stat-view-servlet.login-username", "admin");
// 从环境配置中获取监控页面登录密码,默认值为"123456"
String loginPassword = environment.getProperty("spring.datasource.druid.stat-view-servlet.login-password", "123456");
// 配置IP白名单:允许访问监控页面的IP地址,空字符串表示允许所有IP访问
servletRegistrationBean.addInitParameter("allow",
environment.getProperty("spring.datasource.druid.stat-view-servlet.allow", ""));
// 配置IP黑名单:拒绝访问监控页面的IP地址,空字符串表示不拒绝任何IP
servletRegistrationBean.addInitParameter("deny",
environment.getProperty("spring.datasource.druid.stat-view-servlet.deny", ""));
// 设置监控页面登录用户名
servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
// 设置监控页面登录密码
servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
// 设置是否允许重置监控数据:false表示不允许重置,防止误操作清空统计数据
servletRegistrationBean.addInitParameter("resetEnable",
environment.getProperty("spring.datasource.druid.stat-view-servlet.reset-enable", "false"));
log.info("Druid监控Servlet初始化完成,访问路径: /druid/");
log.info("Druid监控登录用户名: {}", loginUsername);
return servletRegistrationBean;
}
/**
* Druid 监控过滤器配置Bean
* <p>
* 功能:配置Druid的Web统计过滤器,用于收集Web请求和SQL执行统计信息
* <p>
* 过滤器作用:
* - 统计Web应用的URI请求信息
* - 统计Session相关信息
* - 统计SQL执行情况
* - 排除静态资源,避免统计干扰
*
* @return FilterRegistrationBean<WebStatFilter> Druid统计过滤器注册Bean
*/
@Bean
@ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true", matchIfMissing = true)
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
log.info("初始化Druid统计过滤器...");
// 创建过滤器注册Bean
FilterRegistrationBean<WebStatFilter> filterRegistrationBean =
new FilterRegistrationBean<>(new WebStatFilter());
// 配置过滤器拦截所有请求
filterRegistrationBean.addUrlPatterns("/*");
// 配置排除路径:不统计静态资源和Druid监控路径
// 这些路径的请求不会被Druid统计,避免干扰统计数据
filterRegistrationBean.addInitParameter("exclusions",
environment.getProperty("spring.datasource.druid.web-stat-filter.exclusions",
"*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"));
log.info("Druid统计过滤器初始化完成");
return filterRegistrationBean;
}
/**
* 获取所有数据源映射
* <p>
* 功能:提供数据源映射的只读视图,用于监控、管理和调试
* <p>
* 设计考虑:
* - 返回新的HashMap实例,避免外部代码修改内部数据源映射
* - 提供数据源状态信息给监控系统
* - 支持动态数据源的管理和调试
* - 便于在运行时查看所有可用的数据源
*
* @return Map<String, DataSource> 数据源名称到数据源实例的映射副本
*/
public Map<String, DataSource> getDataSourceMap() {
return new HashMap<>(dataSourceMap);
}
/**
* 数据源配置验证器Bean
* <p>
* 功能:在应用启动时验证数据源配置是否正确
* <p>
* 验证内容:
* - 数据源是否成功创建
* - 数据源映射是否正确构建
* - 配置属性是否正确加载
*
* @return DataSourceConfigValidator 配置验证器实例
*/
@Bean
public DataSourceConfigValidator dataSourceConfigValidator() {
return new DataSourceConfigValidator(this);
}
/**
* 数据源配置验证器内部类
* <p>
* 功能:在构造时执行配置验证,确保数据源配置正确
* <p>
* 设计模式:使用内部类封装验证逻辑,避免污染外部类
*/
public static class DataSourceConfigValidator {
private final DataSourceConfig dataSourceConfig;
/**
* 构造函数 - 执行配置验证
*
* @param dataSourceConfig 数据源配置实例
*/
public DataSourceConfigValidator(DataSourceConfig dataSourceConfig) {
this.dataSourceConfig = dataSourceConfig;
// 在构造时立即执行验证
validateConfiguration();
}
/**
* 配置验证方法
* <p>
* 验证步骤:
* 1. 获取数据源映射
* 2. 检查数据源数量
* 3. 记录验证结果
* <p>
* 异常处理:如果验证失败,抛出IllegalStateException阻止应用启动
*/
private void validateConfiguration() {
log.info("开始验证数据源配置...");
try {
// 获取数据源映射
Map<String, DataSource> dataSourceMap = dataSourceConfig.getDataSourceMap();
// 记录验证结果
log.info("数据源配置验证成功,已加载 {} 个数据源: {}",
dataSourceMap.size(), dataSourceMap.keySet());
} catch (Exception e) {
log.error("数据源配置验证失败", e);
// 抛出运行时异常,阻止应用启动
throw new IllegalStateException("数据源配置验证失败", e);
}
log.info("数据源配置验证完成");
}
// /**
// * 配置验证方法 - 增强版
// */
// private void validateConfiguration() {
// log.info("开始验证数据源配置...");
//
// try {
// Map<String, DataSource> dataSourceMap = dataSourceConfig.getDataSourceMap();
//
// // 验证主数据源必须存在
// if (!dataSourceMap.containsKey(DataSourceType.MASTER.name())) {
// throw new IllegalStateException("主数据源配置缺失,系统无法启动");
// }
//
// // 验证数据源数量
// int enabledCount = dataSourceMap.size();
// log.info("数据源配置验证成功,已启用 {} 个数据源: {}", enabledCount, dataSourceMap.keySet());
//
// // 验证每个数据源的可用性
// validateDataSourcesAvailability(dataSourceMap);
//
// } catch (Exception e) {
// log.error("数据源配置验证失败", e);
// throw new IllegalStateException("数据源配置验证失败: " + e.getMessage(), e);
// }
//
// log.info("数据源配置验证完成");
// }
//
// /**
// * 验证数据源可用性
// */
// private void validateDataSourcesAvailability(Map<String, DataSource> dataSourceMap) {
// for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
// String dataSourceName = entry.getKey();
// DataSource dataSource = entry.getValue();
//
// try {
// // 测试数据源连接
// if (dataSource.getConnection() != null) {
// log.debug("数据源 '{}' 连接测试成功", dataSourceName);
// }
// } catch (Exception e) {
// log.warn("数据源 '{}' 连接测试失败: {}", dataSourceName, e.getMessage());
// // 对于主数据源,连接失败应该阻止启动
// if (DataSourceType.MASTER.name().equals(dataSourceName)) {
// throw new IllegalStateException("主数据源连接失败: " + e.getMessage(), e);
// }
// }
// }
// }
}
}
@@ -0,0 +1,150 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | 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.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* <p>
* 数据源参数属性配置
* </p>
*
* @author 小蚂蚁云团队
* @since 2024-05-25
*/
@Configuration
public class DruidProperties {
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;
@Value("${spring.datasource.druid.minIdle}")
private int minIdle;
@Value("${spring.datasource.druid.maxActive}")
private int maxActive;
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource) {
// 配置初始化大小、最小、最大
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
// 配置获取连接等待超时的时间
datasource.setMaxWait(maxWait);
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// 配置一个连接在池中最小、最大生存的时间,单位是毫秒
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
// 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
datasource.setValidationQuery(validationQuery);
// 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
datasource.setTestWhileIdle(testWhileIdle);
// 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
datasource.setTestOnBorrow(testOnBorrow);
// 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}