Springboot项目Druid运行时动态连接多数据源的功能
项目支持多数据库连接是个很常见的需求,这不仅是要在编译前连已经知道的多个数据库,有时还要在程序运行时连后期增加的多个数据源来获得数据。
一、编译前注册数据库连接
1.引入依赖包
<!-- springboot 3.x --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!-- springboot 2.x --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId>
</dependency>
2.配置文件
一般本地应用可能有多个数据源,比如,需要支持读写分离的主从数据库配置,可能的application-druid.yml
配置文件代码如下:
# 数据源配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdynamic:primary: MASTERdatasource:# 主库数据源MASTER:url: jdbc:mysql://127.0.0.1/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456# 从库数据源SLAVE:url: jdbc:mysql://127.0.0.1/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456druid:# 初始连接数initialSize: 5# 最小连接池数量minIdle: 10# 最大连接池数量maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置连接超时时间connectTimeout: 30000# 配置网络超时时间socketTimeout: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis: 900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsewebStatFilter:enabled: truestatViewServlet:enabled: true# 设置白名单,不填则允许所有访问allow:url-pattern: /druid/*# 控制台管理用户名和密码login-username: ruoyilogin-password: 123456filter:stat:enabled: true# 慢SQL记录log-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:config:multi-statement-allow: true
3.配置DruidConfig
/*** druid 配置多数据源* */
@Configuration
public class DruidConfig
{/** 主数据库配置 */@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/** 从数据库配置 */@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/** 动态数据源 */@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object, Object> targetDataSources = new HashMap<>();// 加入主数据库配置targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);// 加入从数据库配置setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);}/*** 设置数据源* * @param targetDataSources 备选数据源集合* @param sourceName 数据源名称* @param beanName bean名称*/public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName){try{DataSource dataSource = SpringUtils.getBean(beanName);targetDataSources.put(sourceName, dataSource);}catch (Exception e){}}
}
4.配置DynamicDataSource
新建DynamicDataSource
类来继承AbstractRoutingDataSource
类。
继承自 Spring 提供的 AbstractRoutingDataSource,通过重写 determineCurrentLookupKey 方法,根据上下文信息动态选择当前使用的数据源。
/*** 动态数据源类,用于实现多数据源切换。*/
public class DynamicDataSource extends AbstractRoutingDataSource {/*** 构造函数,初始化默认数据源和目标数据源映射。** @param defaultTargetDataSource 默认数据源,在未指定其他数据源时使用* @param targetDataSourceMap 目标数据源映射,包含多个可选的数据源*/public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSourceMap) {// 设置默认数据源super.setDefaultTargetDataSource(defaultTargetDataSource);// 设置目标数据源映射super.setTargetDataSources(targetDataSourceMap);// 初始化父类属性super.afterPropertiesSet();}/*** 确定当前使用的数据源键。* 该方法会根据上下文中的数据源类型来决定使用哪个数据源。** @return 当前使用的数据源键*/@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}}
5.配置DynamicDataSourceContextHolder
例如在web应用这种多线程的环境中,每个线程需要执行访问的数据库都不一样。因此需要给每个线程设置独立变量。
新建DynamicDataSourceContextHolder
文件,代码如下:
/*** 数据源切换处理* */
public class DynamicDataSourceContextHolder
{public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源的变量*/public static void setDataSourceType(String dsType){log.info("切换到{}数据源", dsType);CONTEXT_HOLDER.set(dsType);}/*** 获得数据源的变量*/public static String getDataSourceType(){return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void clearDataSourceType(){CONTEXT_HOLDER.remove();}
}
6.配置AOP切面类
通过 AOP 拦截带有 @DataSource
注解的方法或类,在方法执行前设置数据源,在方法执行后清除数据源,确保每个请求都能正确使用指定的数据源。
代码如下:
/*** 多数据源处理切面类。*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect {protected Logger logger = LoggerFactory.getLogger(getClass());/*** 定义切入点,匹配带有 @DataSource 注解的方法或类。*/@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"+ "|| @within(com.ruoyi.common.annotation.DataSource)")public void dsPointCut() {}/*** 环绕通知,拦截 dsPointCut 切入点匹配的方法。* 在方法执行前设置数据源类型,在方法执行后清除数据源类型。** @param point 当前连接点对象* @return 方法返回值* @throws Throwable 如果方法执行过程中抛出异常*/@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {DataSource dataSource = getDataSource(point);if (StringUtils.isNotNull(dataSource)) {// 根据注解配置设置数据源类型if ("".equals(dataSource.name())) {DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());} else {DynamicDataSourceContextHolder.setDataSourceType(dataSource.name());}}try {// 执行目标方法return point.proceed();} finally {// 方法执行完毕后清除数据源类型DynamicDataSourceContextHolder.clearDataSourceType();}}/*** 获取当前方法或类上的 @DataSource 注解配置。** @param point 当前连接点对象* @return 数据源配置信息*/public DataSource getDataSource(ProceedingJoinPoint point) {MethodSignature signature = (MethodSignature) point.getSignature();// 从方法上查找 @DataSource 注解DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(dataSource)) {return dataSource;}// 如果方法上没有找到,则从类上查找 @DataSource 注解return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);}
}
7.使用方式
现在可以通过注解选择要使用哪个数据库。
在需要使用多数据源方法或类上添加@DataSource
注解,其中value用来表示数据源。
@DataSource(value = DataSourceType.SLAVE)
public List<SysUser> selectUserList(SysUser user)
{return userMapper.selectUserList(user);
}
@Service
@DataSource(value = DataSourceType.SLAVE)
public class SysUserServiceImpl{}
或者手动切换数据源
public List<SysUser> selectUserList(SysUser user)
{DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());List<SysUser> userList = userMapper.selectUserList(user);DynamicDataSourceContextHolder.clearDataSourceType();return userList;
}
二、运行时访问数据库
前面访问多数据源的方式必须提前在yaml
这样的配置文件中提前填写好数据库的连接属性。
但是类似BI展示这样的软件项目可能得访问十几个业务的数据源,并且别的数据源的访问信息还时刻在变。这样的话,就不方便把数据库的连接属性提前写在配置文件中了。
1.配置数据管理工具
@Configuration
public class DataSourceManagement implements InitializingBean {protected final Logger logger = LoggerFactory.getLogger(DataSourceManagement.class);/*** 额外数据源*/private Map<String, DataSource> targetDataSources = new HashMap<>();/*** 配置文件的数据源*/@Autowiredprivate DynamicDataSourceProperties dataSourceProperties;@Autowiredprivate CreateDataSource c;@Autowired(required = false)private AfterCreateDataSource afterCreateDataSource;private DynamicDataSource dynamicDataSource;/*** 创建并返回动态数据源实例。** @return 动态数据源实例*/@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource() {Map<Object, Object> targetDataSourceMap = new HashMap<>(targetDataSources);this.dynamicDataSource = new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), targetDataSourceMap);return this.dynamicDataSource;}/*** 验证数据源是否可用。** @param dataSource 要验证的数据源*/public void validateDataSource(DataSource dataSource) {try (Connection conn = dataSource.getConnection()) {String validationQuery = "SELECT 1";try (Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(validationQuery)) {if (!(rs.next() && rs.getInt(1) == 1)) {throw new RuntimeException("数据源连接验证失败:查询结果不正确");}}} catch (SQLException e) {throw new RuntimeException("数据源连接验证失败", e);}}/*** 在所有依赖项设置完成后执行此方法。* 初始化所有配置的数据源,并进行连接验证。*/@Overridepublic void afterPropertiesSet() throws Exception {dataSourceProperties.getDatasource().forEach((name, props) -> {Properties properties = dataSourceProperties.build(props);CommonDataSource commonDataSource = c.createDataSource(name, properties);if (afterCreateDataSource != null) {afterCreateDataSource.afterCreateDataSource(name, properties, commonDataSource);}DataSource dataSource = (DataSource) commonDataSource;logger.info("数据源:{} 校验中.......", name);// 计时long start = System.currentTimeMillis();validateDataSource(dataSource);logger.info("数据源:{} 链接成功,耗时:{}ms", name, System.currentTimeMillis() - start);this.putDataSource(name, dataSource);});}/*** 添加或更新一个数据源,并创建相应的SqlSessionFactory。** @param name 数据源名称* @param dataSource 数据源实例*/public void putDataSource(String name, DataSource dataSource) {targetDataSources.put(name, dataSource);}/*** 添加一个新的数据源。** @param name 数据源名称* @param properties 数据源属性*/public void addDataSourceWithCheck(String name, Properties properties) {if (targetDataSources.containsKey(name)) {logger.info("数据源" + name + "之前已经创建,准备测试数据源是否正常...");//throw new RuntimeException("数据源已存在");} else {CommonDataSource commonDataSource = c.createDataSource(name, properties);if (afterCreateDataSource != null) {afterCreateDataSource.afterCreateDataSource(name, properties, commonDataSource);}DataSource dataSource = (DataSource) commonDataSource;logger.info("数据源:{} 校验中.......", name);// 计时long start = System.currentTimeMillis();validateDataSource(dataSource);logger.info("数据源:{} 链接成功,耗时:{}ms", name, System.currentTimeMillis() - start);this.putDataSource(name, dataSource);HashMap<Object, Object> objectHashMap = new HashMap<>(this.targetDataSources);// 将map赋值给父类的TargetDataSourcesthis.dynamicDataSource.setTargetDataSources(objectHashMap);// 重要:将TargetDataSources中的连接信息放入resolvedDataSources管理,否则无法切换数据源this.dynamicDataSource.afterPropertiesSet();logger.debug("添加数据源成功,名称:{}", name);}}}
2.创建数据源
通过 DruidXADataSource 创建数据源,并使用 DruidConfig 和 DynamicDataSourceProperties 配置数据源属性。
/*** 数据源创建类,实现 CreateDataSource 接口。* */
@Component
public class DataSourceCreate implements CreateDataSource {@Autowiredprivate DynamicDataSourceProperties properties;@Autowiredprivate DruidConfig druidConfig;/*** 创建数据源的方法。** @param name 数据源名称* @param prop 数据源配置属性* @return 创建的数据源对象*/@Overridepublic DataSource createDataSource(String name, Properties prop) {// 创建 DruidXADataSource 实例DruidXADataSource dataSource = new DruidXADataSource();// 将创建的数据源添加到 DruidConfig 的数据源列表中druidConfig.getDruidDataSources().add(dataSource);// 设置数据源的连接属性dataSource.setConnectProperties(prop);// 使用 DynamicDataSourceProperties 配置数据源的其他属性properties.setProperties(dataSource, prop);// 返回创建的数据源对象return dataSource;}
}
3.准备实体
准备个存储数据库连接属性的实体,用于接收信息。
@Data
public class DataSource {private String id;private String connIp;private String connPort;private String connDbName;private String connUserName;private String connPassWord;private String connName;private String connType;private String connProperty;private String connDriverClass;
}
4.调用方式
写一个服务类,将从某个存储数据库连接信息的表里查出所有数据源。然后根据连接属性创建动态数据源。
@Service
public class DBChangeServiceImpl implements IDBChangeService {private final Logger logger = LoggerFactory.getLogger(DBChangeServiceImpl.class);@AutowiredDataSourceMapper dataSourceMapper;@Autowiredprivate DataSourceManagement dataSourceManagement;/*** 获取数据源* @return*/@Overridepublic List<DataSource> get() {return dataSourceMapper.get();}/*** 切换数据源* @param datasourceId* @return*/@Overridepublic boolean changeDb(String datasourceId) {//清除本线程的数据源DynamicDataSourceContextHolder.clearDataSourceType();logger.debug("清除本线程的数据源");//获取所有数据源List<DataSource> dataSourcesList = dataSourceMapper.get();logger.debug("获取到的数据源列表: {}", JSON.toJSONString(dataSourcesList));//遍历所有数据源,切换到指定数据源for (DataSource dataSource : dataSourcesList) {if (dataSource.getId().equals(datasourceId)) {logger.info(JSON.toJSONString(dataSource));logger.info("需要使用的的数据源已经找到,datasourceId是:" + dataSource.getId());Properties properties = buildDataSourceProperties(dataSource);//创建数据源连接&检查 若存在则不需重新创建try {// 使用DataSourceManagement来创建数据源并检查dataSourceManagement.addDataSourceWithCheck(dataSource.getId(), properties);// 切换数据源DynamicDataSourceContextHolder.setDataSourceType(dataSource.getId());logger.info("标记切换到数据源:"+datasourceId);return true;} catch (Exception e) {logger.error("数据源切换失败", e);e.printStackTrace();}}}logger.debug("未找到目标数据源");return false;}private Properties buildDataSourceProperties(DataSource dataSource) {Properties properties = new Properties();properties.setProperty("url", "jdbc:mysql://" + dataSource.getConnIp() + ":" + dataSource.getConnPort() + "/" + dataSource.getConnDbName());properties.setProperty("username", dataSource.getConnUserName());properties.setProperty("password", dataSource.getConnPassWord());properties.setProperty("driverClassName", dataSource.getConnDriverClass());// 添加其他必要的属性return properties;}}
切换数据源执行sql语句后,记得把数据源再切回去。
public AjaxResult runWithSql(String datasourceid, String sql){//切换到数据库dbChangeService.changeDb(datasourceid);System.out.println("Current DataSource Type: " + DynamicDataSourceContextHolder.getDataSourceType());System.out.println("RunWithSql: "+sql);List<Map<String, Object>> maps = execSqlService.runWithSql(sql);System.out.println("=======>"+JSON.toJSONString(maps));//切回主数据源DynamicDataSourceContextHolder.clearDataSourceType();return AjaxResult.success(maps);}
可以自行测试,发现连接成功了。
相关文章:

Springboot项目Druid运行时动态连接多数据源的功能
项目支持多数据库连接是个很常见的需求,这不仅是要在编译前连已经知道的多个数据库,有时还要在程序运行时连后期增加的多个数据源来获得数据。 一、编译前注册数据库连接 1.引入依赖包 <!-- springboot 3.x --><dependency><groupId&g…...
字符串匹配——KMP算法
前言 刷到字符串匹配的力扣题了【28. 实现 strStr() 】,这题简单吧用库函数做就可以,说难吧,就得引出大名鼎鼎的线性匹配算法——KMP。 目录 KMP 算法背景与原理算法优势 前缀表1. 构建Next数组2. 搜索匹配 KMP 算法背景与原理 KMP&#x…...

Qt开发技术【下拉复选框 MultiSelectComboBox 自定义全选项】
继承ComboBox完成下拉复选框 自定义全选项 效果图 整个控件继承于QCombobox类。主要修改QLineEdit、QListWidget这两部分,QComboBox提供如下接口,可以将这两部分设置为新建的QLineEdit、QListWidget对象 CMultiSelectComboBox::CMultiSelectComboBo…...

20_HTML5 SSE --[HTML5 API 学习之旅]
HTML5 Server-Sent Events (SSE) 是一种技术,它允许服务器向浏览器推送更新。与传统的轮询不同,SSE提供了真正的单向实时通信通道:服务器可以主动发送数据到客户端,而不需要客户端发起请求。这对于实现实时更新的应用非常有用&…...

jetson Orin nx + yolov8 TensorRT 加速量化 环境配置
参考【Jetson】Jetson Orin NX纯系统配置环境-CSDN博客 一 系统环境配置: 1.更换源: sudo vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list2.更新源: sudo apt upgradesudo apt updatesudo apt dist-upgrade sudo apt-get updat…...

Android Studio IDE环境配置
需要安装哪些东西: Java jdk Java Downloads | OracleAndroid Studio 下载 Android Studio 和应用工具 - Android 开发者 | Android DevelopersAndroid Sdk 现在的Android Studio版本安装时会自动安装,需要注意下安装的路径Android Studio插件…...
PTA 7-2 0/1背包问题(回溯法) 作者 王东 单位 贵州师范学院
0/1背包问题。给定一载重量为W的背包及n个重量为wi、价值为vi的物体,1≤i≤n,要求重量和恰好为W具有最大的价值。 输入格式: 第一行输入背包载重量W及背包个数n,再依次输入n行,每行为背包重量wi和价值vi。 输出格式: 第一行输出装入背包内…...

Matlab环形柱状图
数据准备: 名称 数值 Aa 21 Bb 23 Cc 35 Dd 47 保存为Excel文件后: % Load data from Excel file filename data.xlsx; % Ensure the file is in the current folder or provide full path dataTable readtable(filena…...

【AI大模型】探索GPT模型的奥秘:引领自然语言处理的新纪元
目录 🍔 GPT介绍 🍔 GPT的架构 🍔 GPT训练过程 3.1 无监督的预训练语言模型 3.2 有监督的下游任务fine-tunning 🍔 小结 学习目标 了解什么是GPT.掌握GPT的架构.掌握GPT的预训练任务. 🍔 GPT介绍 GPT是OpenAI公…...
5.Python爬虫相关
爬虫 爬虫原理 爬虫,又称网络爬虫,是一种自动获取网页内容的程序。它模拟人类浏览网页的行为,发送HTTP请求,获取网页源代码,再通过解析、提取等技术手段,获取所需数据。 HTTP请求与响应过程 爬虫首先向…...
Windows系统上配置eNSP环境的详细步骤
华为eNSP(Enterprise Network Simulation Platform)是一款针对华为数通网络设备的网络仿真平台,用于辅助工程师进行网络技术学习、方案验证和故障排查等工作。以下是在Windows系统上配置eNSP环境的详细步骤: 1. 准备工作 下载安…...

Database.NET——一款轻量级多数据库客户端工具
文章目录 Database.NET简介下载使用使用场景总结 Database.NET简介 Database.NET 是一个功能强大且易于使用的数据库管理工具,适用于多种数据库系统。它为开发者和数据库管理员提供了一个统一的界面,可以方便地管理和操作不同类型的数据库。 支持的数据…...

新浪微博C++面试题及参考答案
多态是什么?请详细解释其实现原理,例如通过虚函数表实现。 多态是面向对象编程中的一个重要概念,它允许不同的对象对同一消息或函数调用做出不同的响应,使得程序具有更好的可扩展性和灵活性。 在 C 中,多态主要通过虚函…...

计算机视觉目标检测-1
文章目录 摘要Abstract1.目标检测任务描述1.1 目标检测分类算法1.2 目标定位的简单实现思路1.2.1 回归位置 2.R-CNN2.1 目标检测-Overfeat模型2.1.1 滑动窗口 2.2 目标检测-RCNN模型2.2.1 非极大抑制(NMS) 2.3 目标检测评价指标 3.SPPNet3.1 spatial pyr…...

【物联网技术与应用】实验15:电位器传感器实验
实验15 电位器传感器实验 【实验介绍】 电位器可以帮助控制Arduino板上的LED闪烁的时间间隔。 【实验组件】 ● Arduino Uno主板* 1 ● 电位器模块* 1 ● USB电缆*1 ● 面包板* 1 ● 9V方型电池* 1 ● 跳线若干 【实验原理】 模拟电位器是模拟电子元件,模…...

java常用类(上)
笔上得来终觉浅,绝知此事要躬行 🔥 个人主页:星云爱编程 🔥 所属专栏:javase 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 一、包装类 1.1包装类…...
包管理工具npm、yarn、pnpm、cnpm详解
1. 包管理工具 1.1 npm # 安装 $ node 自带 npm# 基本用法 npm install package # 安装包 npm install # 安装所有依赖 npm install -g package # 全局安装 npm uninstall package # 卸载包 npm update package # 更新包 npm run script #…...

CI/CD是什么?
CI/CD 定义 CI/CD 代表持续集成和持续部署(或持续交付)。它是一套实践和工具,旨在通过自动化构建、测试和部署来改进软件开发流程,使您能够更快、更可靠地交付代码更改。 持续集成 (CI):在共享存储库中自动构建、测试…...
[Java]合理封装第三方工具包(附视频)
-1.视频链接 视频版: 视频版会对本文章内容进行详细解释 [Java]合理封装第三方工具包_哔哩哔哩_bilibili 0.核心思想 对第三方工具方法进行封装,使其本地化,降低记忆和使用成本 1.背景 在我们的项目中,通常会引用一些第三方工具包,或者是使用jdk自带的一些工具类 例如: c…...

常规配置、整合IDEA
目录 Redis常规配置 tcp-keepalive security Jedis RedisTemplate 连接池技术 Lua脚本 Jedis集群 Redis应用问题&解决方案 缓存穿透 缓存击穿 缓存雪崩 分布式锁 Redis实现分布式锁 Redis新功能 ACL Redis常规配置 tcp-keepalive security redis.conf中…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...