@ -41,7 +41,10 @@ import java.util.stream.Collectors;
import java.util.stream.Stream ;
/ * *
* 注 册 数 据 源 实 现 类
* 注 册 数 据 源 实 现 类 , 该 类 实 现 了 DatabaseService 接 口 以 及 ApplicationRunner 接 口 。
* 实 现 DatabaseService 接 口 用 于 提 供 与 数 据 库 相 关 的 业 务 逻 辑 操 作 , 比 如 解 密 数 据 库 、 注 册 数 据 源 等 功 能 。
* 实 现 ApplicationRunner 接 口 可 以 在 Spring Boot 应 用 启 动 后 执 行 特 定 的 初 始 化 或 业 务 逻 辑 代 码 。
* 在 整 个 项 目 架 构 中 处 于 服 务 层 , 负 责 协 调 不 同 服 务 ( 如 解 密 服 务 、 微 信 服 务 、 用 户 服 务 等 ) 之 间 的 交 互 , 完 成 复 杂 的 业 务 流 程 , 例 如 数 据 库 解 密 及 后 续 数 据 源 注 册 等 操 作 。
*
* @author xcs
* @date 2023 年 12 月 25 日 19 : 30 : 26
@ -51,45 +54,58 @@ import java.util.stream.Stream;
@RequiredArgsConstructor
public class DatabaseServiceImpl implements DatabaseService , ApplicationRunner {
// 通过依赖注入获取解密服务,用于执行数据库解密相关的具体操作,比如对微信数据库文件进行解密处理。
private final DecryptService decryptService ;
// 通过依赖注入获取微信服务,可能用于获取微信相关的配置信息、密钥等内容,辅助数据库解密等操作的进行。
private final WeChatService weChatService ;
// 通过依赖注入获取用户服务,用于执行与用户相关的操作,例如保存用户信息等,在数据库解密完成后可能需要记录相关用户信息。
private final UserService userService ;
@Override
public void decrypt ( SseEmitter emitter , DecryptDTO decryptDTO ) {
// 文件分隔符
// 文件分隔符 , 通过获取默认文件系统的分隔符, 用于后续拼接文件路径, 确保在不同操作系统下路径的正确性( 不同系统文件分隔符不同, 如Windows是'\', Linux是'/')。
String separator = FileSystems . getDefault ( ) . getSeparator ( ) ;
// 微信目录
// 微信目录 , 根据传入的解密参数( DecryptDTO) 中的基础路径( basePath) 和微信用户ID( wxId) 拼接出微信数据库文件所在的完整目录路径。
String dbPath = decryptDTO . getBasePath ( ) + separator + decryptDTO . getWxId ( ) ;
// 秘钥
// 秘钥, 调用weChatService的getKey方法, 传入进程ID( pid) 和微信数据库文件目录路径( dbPath) 作为参数, 尝试获取微信数据库的解密密钥,
// 该密钥用于后续对数据库文件的解密操作。
String key = weChatService . getKey ( decryptDTO . getPid ( ) , dbPath ) ;
// 获取微信秘钥失败
// 获取微信秘钥失败 ,判断获取到的密钥是否为空字符串,如果为空则表示获取微信密钥失败,进入以下处理流程。
if ( StrUtil . isBlank ( key ) ) {
try {
// 向前端发送错误响应信息, 通过emitter发送一个ResponseVO对象, 其中包含错误码( -1) 和错误消息( "获取微信秘钥失败,请稍后再试。") ,
// 并指定消息的媒体类型为APPLICATION_JSON, 告知前端获取密钥出现问题。
emitter . send ( ResponseVO . error ( - 1 , "获取微信秘钥失败,请稍后再试。" ) , MediaType . APPLICATION_JSON ) ;
} catch ( IOException e ) {
// 如果在发送响应信息过程中出现IO异常, 将该异常包装为运行时异常抛出, 以便在更上层进行统一的异常处理。
throw new RuntimeException ( e ) ;
} finally {
// 无论是否发送成功, 都标记SseEmitter完成, 释放相关资源, 结束与前端的此次通信。
emitter . complete ( ) ;
}
return ;
}
// 扫描目录
// 扫描目录 , 在微信数据库文件所在目录下, 拼接出要扫描的具体子目录路径( 这里是MSG目录) , 后续将遍历该目录下的文件进行解密等操作。
String scanPath = dbPath + separator + "MSG" ;
// 输出目录
// 输出目录 , 调用DirUtil的getDbDir方法, 传入微信用户ID( wxId) 作为参数, 获取解密后数据库文件的输出目录路径, 用于存放解密后的文件。
String outputPath = DirUtil . getDbDir ( decryptDTO . getWxId ( ) ) ;
// 使用Files.walk创建一个Stream来遍历给定路径下的所有文件和目录
// 使用Files.walk创建一个Stream来遍历给定路径下的所有文件和目录, 通过Paths.get方法将字符串形式的扫描路径转换为Path对象,
// 然后利用Files.walk方法获取一个包含该路径下所有文件和子目录的Stream, 方便后续对其进行过滤和处理操作。
try ( Stream < Path > stream = Files . walk ( Paths . get ( scanPath ) ) ) {
// 过滤出非目录的文件
// 过滤出非目录的文件, 调用getWeChatDb方法, 传入文件流( stream) 和输出目录路径( outputPath) 作为参数,
// 对遍历到的文件和目录进行筛选, 只保留文件( 非目录) 信息, 并转换为DecryptBO对象列表, 该列表包含了要解密的文件相关信息。
List < DecryptBO > decryptBOList = getWeChatDb ( stream , outputPath ) ;
// 遍历解密
// 遍历解密 ,对获取到的要解密的文件列表进行循环遍历,逐个处理每个文件的解密及相关后续操作。
for ( int i = 0 ; i < decryptBOList . size ( ) ; i + + ) {
DecryptBO decryptBO = decryptBOList . get ( i ) ;
// 计算进度百分比
// 计算进度百分比, 根据当前处理的文件索引( i) 和文件列表总大小( decryptBOList.size())计算出当前的解密进度百分比,
// 用于后续向前端发送进度信息展示给用户。
int currentProgress = ( ( i + 1 ) * 100 ) / decryptBOList . size ( ) ;
// 当前要处理的文件
// 当前要处理的文件 , 根据DecryptBO对象中的输入文件路径信息创建一个File对象, 代表当前正在处理的数据库文件实体, 方便后续获取文件相关属性。
File currentFile = new File ( decryptBO . getInput ( ) ) ;
// 响应给前端的对象
// 响应给前端的对象, 使用建造者模式( Builder Pattern) 创建一个DecryptVO对象, 设置该对象的文件名、文件大小、文件总数以及当前进度等属性,
// 用于将解密进度相关信息封装后发送给前端展示。
DecryptVO decryptVO = DecryptVO . builder ( )
. fileName ( FileUtil . getName ( currentFile ) )
. fileSize ( FileUtil . readableFileSize ( currentFile ) )
@ -97,15 +113,21 @@ public class DatabaseServiceImpl implements DatabaseService, ApplicationRunner {
. currentProgress ( currentProgress )
. build ( ) ;
try {
// 向前端发送解密进度信息, 通过emitter将包含解密进度信息的ResponseVO对象发送给前端, 告知前端当前文件的解密进度情况,
// 消息的媒体类型同样指定为APPLICATION_JSON。
emitter . send ( ResponseVO . ok ( decryptVO ) , MediaType . APPLICATION_JSON ) ;
} catch ( IOException ignore ) {
// 如果发送过程中出现IO异常, 暂时忽略该异常( 这里可能是考虑到不影响整体的解密流程继续进行, 只是前端可能无法及时获取到这一次的进度信息) 。
}
// 解密
// 解密, 调用decryptService的wechatDecrypt方法, 传入获取到的解密密钥( key) 和当前要解密的文件信息对象( decryptBO) ,
// 执行对当前数据库文件的解密操作。
decryptService . wechatDecrypt ( key , decryptBO ) ;
// 注册数据源
// 注册数据源, 调用registerDataSource方法, 传入解密后文件的输出路径( decryptBO.getOutput()) ,
// 将解密后的数据库文件注册为应用中的数据源,以便后续可以通过该数据源进行数据库相关操作。
registerDataSource ( decryptBO . getOutput ( ) ) ;
}
// 保存用户
// 保存用户, 调用userService的saveUser方法, 使用建造者模式创建一个UserBO对象并传入相关用户信息( 如基础路径、账号、手机号等) ,
// 将解密相关的用户信息保存到系统中,可能用于记录哪些用户的数据库文件已经完成了解密等操作。
userService . saveUser ( UserBO . builder ( )
. basePath ( decryptDTO . getBasePath ( ) )
. account ( decryptDTO . getAccount ( ) )
@ -115,21 +137,36 @@ public class DatabaseServiceImpl implements DatabaseService, ApplicationRunner {
. wxId ( decryptDTO . getWxId ( ) )
. build ( ) ) ;
} catch ( Exception e ) {
// 如果在整个解密、发送进度信息、注册数据源或保存用户信息等操作过程中出现任何异常, 使用log.error记录错误信息,
// 方便后续排查问题,其中"Sqlite database decryption failed"表示是SQLite数据库解密失败的提示内容, e则是捕获到的具体异常对象。
log . error ( "Sqlite database decryption failed" , e ) ;
} finally {
// 无论是否出现异常, 都标记SseEmitter完成, 释放相关资源, 结束与前端的此次通信。
emitter . complete ( ) ;
}
}
/ * *
* 根 据 微 信 用 户 ID ( wxId ) 获 取 对 应 的 数 据 库 信 息 列 表 , 具 体 是 查 找 指 定 微 信 用 户 目 录 下 的 所 有 . db 后 缀 的 数 据 库 文 件 ,
* 将 其 相 关 信 息 ( 文 件 路 径 、 文 件 大 小 等 ) 封 装 到 DatabaseVO 对 象 中 , 并 以 列 表 形 式 返 回 。
* 如 果 对 应 的 微 信 用 户 数 据 库 目 录 不 存 在 , 则 直 接 返 回 一 个 空 列 表 。
*
* @param wxId 微 信 用 户 ID , 用 于 唯 一 确 定 要 获 取 数 据 库 信 息 的 对 应 微 信 用 户 , 不 同 的 wxId 对 应 不 同 用 户 的 数 据 库 相 关 资 源 。
* @return List < DatabaseVO > 返 回 一 个 包 含 DatabaseVO 对 象 的 列 表 , 每 个 DatabaseVO 对 象 包 含 了 数 据 库 文 件 的 路 径 和 大 小 等 信 息 ,
* 若 指 定 微 信 用 户 的 数 据 库 目 录 不 存 在 则 返 回 一 个 空 列 表 ( 即 Collections . emptyList ( ) ) 。
* /
@Override
public List < DatabaseVO > getDatabase ( String wxId ) {
// 根据微信用户ID获取对应的数据库目录路径, 调用DirUtil的getDbDir方法传入wxId作为参数, 得到存放该微信用户数据库文件的目录路径。
String dbPath = DirUtil . getDbDir ( wxId ) ;
// 不存在目录直接返回
// 不存在目录直接返回 ,判断获取到的数据库目录路径是否存在,如果不存在(即对应的微信用户数据库目录不存在),则直接返回一个空列表,避免后续不必要的操作。
if ( ! FileUtil . exist ( dbPath ) ) {
return Collections . emptyList ( ) ;
}
// 使用Files.walk创建一个Stream来遍历给定路径下的所有文件和目录
// 使用Files.walk创建一个Stream来遍历给定路径下的所有文件和目录, 通过Paths.get方法将字符串形式的数据库目录路径转换为Path对象,
// 然后利用Files.walk方法获取一个包含该路径下所有文件和子目录的Stream, 方便后续对其进行过滤和处理操作。
try ( Stream < Path > stream = Files . walk ( Paths . get ( dbPath ) ) ) {
// 对文件流进行过滤、映射等操作,先过滤出文件名以".db"结尾的文件(即筛选出数据库文件),
// 然后对每个符合条件的文件创建一个DatabaseVO对象, 并设置其文件路径和文件大小属性, 最后收集为一个包含DatabaseVO对象的列表返回。
return stream . filter ( file - > file . toString ( ) . endsWith ( ".db" ) )
. map ( file - > {
DatabaseVO databaseVO = new DatabaseVO ( ) ;
@ -139,76 +176,126 @@ public class DatabaseServiceImpl implements DatabaseService, ApplicationRunner {
} )
. collect ( Collectors . toList ( ) ) ;
} catch ( Exception e ) {
// 如果在遍历目录、获取数据库文件信息等操作过程中出现任何异常, 使用log.error记录错误信息,
// 方便后续排查问题,其中"get database failed"表示获取数据库信息失败的提示内容, e则是捕获到的具体异常对象。
log . error ( "get database failed" , e ) ;
}
// 如果出现异常或者前面的操作没有正常返回结果, 则返回一个空列表, 确保方法的返回值始终符合预期的List<DatabaseVO>类型。
return Collections . emptyList ( ) ;
}
/ * *
* 获 取 微 信 的 db 文 件
* 获 取 微 信 的 db 文 件 的 方 法 , 用 于 从 给 定 的 文 件 流 ( 代 表 一 个 目 录 下 的 所 有 文 件 和 目 录 信 息 ) 中 筛 选 出 符 合 条 件 的 微 信 数 据 库 文 件 ,
* 并 将 其 转 换 为 DecryptBO 对 象 列 表 返 回 。 筛 选 条 件 包 括 文 件 不 是 目 录 且 文 件 名 以 ".db" 结 尾 , 同 时 设 置 每 个 DecryptBO 对 象 的 输 入 输 出 路 径 等 属 性 。
*
* @param stream 目 录
* @param outputPath 输 出 目 录
* @return DecryptBO
* @param stream 目 录 , 以 Stream < Path > 形 式 传 入 , 代 表 要 筛 选 的 文 件 和 目 录 信 息 的 流 , 通 常 是 通 过 Files . walk 方 法 获 取 的 某 个 目 录 下 的 所 有 文 件 和 子 目 录 的 路 径 流 。
* @param outputPath 输 出 目 录 , 以 字 符 串 形 式 传 入 , 用 于 指 定 解 密 后 数 据 库 文 件 的 输 出 存 放 目 录 路 径 , 在 创 建 DecryptBO 对 象 时 会 结 合 文 件 名 等 信 息 设 置 输 出 路 径 属 性 。
* @return List < DecryptBO > 返 回 一 个 包 含 DecryptBO 对 象 的 列 表 , 每 个 DecryptBO 对 象 包 含 了 要 解 密 的 数 据 库 文 件 的 输 入 路 径 ( 原 始 文 件 路 径 ) 和 输 出 路 径 ( 解 密 后 存 放 路 径 ) 等 信 息 ,
* 该 列 表 包 含 了 筛 选 出 来 的 所 有 符 合 条 件 的 微 信 数 据 库 文 件 相 关 信 息 。
* /
private List < DecryptBO > getWeChatDb ( Stream < Path > stream , String outputPath ) {
// 对传入的文件流进行过滤操作, 先过滤掉代表目录的Path对象, 只保留代表文件的Path对象,
// 这样后续处理的都是文件相关信息,便于进一步筛选出数据库文件。
return stream . filter ( file - > ! Files . isDirectory ( file ) )
// 过滤出文件名以.db结尾的文件
// 过滤出文件名以.db结尾的文件 ,在剩下的文件中再次筛选,只保留文件名以".db"结尾的文件,即筛选出微信数据库文件。
. filter ( file - > file . toString ( ) . endsWith ( ".db" ) )
// 将每个符合条件的文件路径映射为DecryptDTO对象
// 将每个符合条件的文件路径映射为DecryptDTO对象, 对每个筛选出来的数据库文件Path对象, 创建一个DecryptBO对象,
// 传入文件的原始路径( toString()方法获取) 作为输入路径, 结合输出目录路径( outputPath) 和文件名创建输出路径, 设置到DecryptBO对象中。
. map ( item - > new DecryptBO ( item . toString ( ) , outputPath + FileUtil . getName ( item . toString ( ) ) ) )
// 转换成List
// 转换成List , 将经过上述处理后得到的所有DecryptBO对象收集为一个列表, 最终返回该列表, 其包含了所有符合条件的微信数据库文件相关信息。
. collect ( Collectors . toList ( ) ) ;
}
/ * *
* Spring Boot 应 用 启 动 后 执 行 的 方 法 , 实 现 了 ApplicationRunner 接 口 的 run 方 法 。
* 该 方 法 主 要 用 于 在 应 用 启 动 时 , 遍 历 当 前 工 作 目 录 下 的 db 目 录 中 的 所 有 . db 后 缀 的 数 据 库 文 件 ,
* 对 每 个 符 合 条 件 的 文 件 调 用 registerDataSource 方 法 进 行 数 据 源 的 动 态 注 册 操 作 , 若 db 目 录 不 存 在 则 直 接 返 回 不 进 行 任 何 操 作 。
*
* @param args 应 用 启 动 时 传 入 的 参 数 , 由 Spring Boot 传 递 进 来 , 不 过 在 当 前 方 法 中 未 对 其 进 行 使 用 , 通 常 可 用 于 接 收 命 令 行 参 数 等 信 息 来 影 响 应 用 启 动 时 的 一 些 行 为 。
* /
@Override
public void run ( ApplicationArguments args ) {
// 获取当前工作目录下的 db 目录
// 获取当前工作目录下的 db目录, 调用DirUtil的getDbDir方法( 无参数形式, 可能获取默认配置下的db目录路径) 获取当前工作目录下存放数据库文件的db目录路径。
String dbPath = DirUtil . getDbDir ( ) ;
// 获得目录
// 获得目录 , 通过Paths.get方法将字符串形式的db目录路径转换为Path对象, 方便后续进行文件相关的操作, 比如判断目录是否存在等。
Path dbDirectory = Paths . get ( dbPath ) ;
// 检查目录是否存在
// 检查目录是否存在 , 判断获取到的db目录对应的Path对象所代表的目录是否真实存在, 如果不存在则直接返回, 不进行后续遍历文件等操作。
if ( ! Files . exists ( dbDirectory ) ) {
return ;
}
// 使用 Files.walk 创建一个 Stream 来遍历给定路径下的所有文件和目录
// 使用Files.walk创建一个Stream来遍历给定路径下的所有文件和目录, 通过Files.walk方法获取一个包含db目录下所有文件和子目录的Stream,
// 用于后续筛选数据库文件并进行数据源注册操作。
try ( Stream < Path > stream = Files . walk ( dbDirectory ) ) {
// 处理文件流
// 处理文件流, 对获取到的文件流进行一系列过滤、遍历操作, 先过滤掉代表目录的Path对象, 再筛选出文件名以".db"结尾的文件,
// 然后对每个符合条件的数据库文件调用registerDataSource方法进行数据源的动态注册操作。
stream . filter ( file - > ! Files . isDirectory ( file ) )
// 过滤出文件名以 .db 结尾的文件
. filter ( file - > file . toString ( ) . endsWith ( ".db" ) )
// 将每个符合条件的文件创建 DataSourceProperty 对象
. forEach ( dbFile - > registerDataSource ( dbFile . toString ( ) ) ) ;
} catch ( Exception e ) {
// 如果在遍历目录、注册数据源等操作过程中出现任何异常, 使用log.error记录错误信息,
// 方便后续排查问题,其中"Failed to register the data source"表示数据源注册失败的提示内容, e则是捕获到的具体异常对象。
log . error ( "Failed to register the data source" , e ) ;
}
}
/ * *
* 动 态 注 册 数 据 源
* 动 态 注 册 数 据 源 的 方 法 , 用 于 将 指 定 路 径 的 数 据 库 文 件 注 册 为 应 用 中 的 数 据 源 , 以 便 后 续 可 以 通 过 该 数 据 源 进 行 数 据 库 相 关 操 作 。
* 该 方 法 会 根 据 数 据 库 文 件 路 径 获 取 对 应 的 微 信 用 户 ID 、 数 据 库 名 称 等 信 息 , 配 置 数 据 源 相 关 属 性 ( 如 连 接 池 参 数 等 ) ,
* 然 后 通 过 Spring 相 关 工 具 类 获 取 数 据 源 创 建 对 象 和 动 态 路 由 数 据 源 对 象 , 最 终 将 创 建 好 的 数 据 源 添 加 到 动 态 路 由 数 据 源 中 完 成 注 册 。
*
* @param dbPath 数 据 库 路 径
* @param dbPath 数 据 库 路 径 , 以 字 符 串 形 式 传 入 , 代 表 要 注 册 为 数 据 源 的 数 据 库 文 件 的 完 整 路 径 , 通 过 该 路 径 可 以 获 取 到 数 据 库 的 相 关 信 息 以 及 确 定 其 唯 一 性 。
* /
private void registerDataSource ( String dbPath ) {
// 从数据库文件路径中获取微信用户ID, 先获取数据库文件路径的父目录( 向上一级) , 再获取该父目录的名称作为微信用户ID,
// 这里假设数据库文件存放目录的上一级目录名称就是对应的微信用户ID, 用于后续区分不同用户的数据源等操作。
String wxId = FileUtil . getName ( FileUtil . getParent ( dbPath , 1 ) ) ;
// 获取数据库名称,直接获取数据库文件路径中的文件名作为数据库名称,用于在数据源配置等过程中标识该数据源对应的具体数据库。
String dbName = FileUtil . getName ( dbPath ) ;
// 创建DruidConfig对象, 用于配置数据源的连接池相关参数, Druid是常用的数据库连接池组件, 这里对其进行一些参数设置。
DruidConfig druidConfig = new DruidConfig ( ) ;
// 设置连接池初始大小, 指定连接池初始化时创建的连接数量为5个, 即应用启动时就会预先创建5个数据库连接, 方便后续使用时直接获取, 提高效率。
druidConfig . setInitialSize ( 5 ) ;
// 设置连接池最小空闲连接数, 保证连接池中最少有5个空闲连接, 避免连接资源过度释放导致后续获取连接时需要重新创建, 影响性能。
druidConfig . setMinIdle ( 5 ) ;
// 设置连接池最大活动连接数, 限制连接池中同时最多能有20个活动连接, 防止过多的连接占用过多系统资源, 造成资源浪费或性能问题。
druidConfig . setMaxActive ( 20 ) ;
// 设置获取连接的最大等待时间( 单位为毫秒) , 当连接池中没有可用连接时, 请求获取连接的线程最多等待60000毫秒( 即60秒) ,
// 超过这个时间如果还没有获取到连接则会抛出异常,避免线程长时间阻塞等待连接。
druidConfig . setMaxWait ( 60000 ) ;
// 设置验证查询语句,用于定期检测连接是否有效,这里设置为"SELECT 1",即通过执行这个简单的查询语句来验证数据库连接是否可用,确保连接的有效性。
druidConfig . setValidationQuery ( "SELECT 1" ) ;
// 设置在空闲时检测连接是否有效,当连接处于空闲状态时,会按照一定的规则(由连接池内部机制决定)周期性地执行验证查询语句来检测连接是否还能正常使用,
// 如果无效则会自动回收该连接并重新创建新的连接补充到连接池中。
druidConfig . setTestWhileIdle ( true ) ;
// 设置在从连接池获取连接时不进行有效性检测,即当应用从连接池中获取连接时,不会执行验证查询语句来再次验证连接是否可用,
// 依赖于连接池在空闲时的检测机制来保证连接的有效性,这样可以提高获取连接的效率,避免每次获取都进行验证带来的性能损耗。
druidConfig . setTestOnBorrow ( false ) ;
// 设置在归还连接到连接池时不进行有效性检测,当应用使用完连接并归还到连接池时,不会再次验证连接是否可用,同样依赖空闲时的检测机制来管理连接有效性。
druidConfig . setTestOnReturn ( false ) ;
// 设置是否对预编译语句进行缓存,开启该功能后,对于相同的预编译语句可以复用缓存中的结果,提高执行效率,减少编译语句的开销。
druidConfig . setPoolPreparedStatements ( true ) ;
// 创建DataSourceProperty对象, 用于设置数据源的基本属性, 比如数据库连接URL、驱动类名、连接池名称等信息, 是配置数据源的重要对象。
DataSourceProperty sourceProperty = new DataSourceProperty ( ) ;
// 设置数据库连接URL, 按照项目中定义的常量( SqliteConstant.URL_PREFIX) 加上传入的数据库文件路径( dbPath) 来拼接出完整的数据库连接URL,
// 用于指定要连接的数据库的具体位置和相关配置信息, 不同类型的数据库其URL格式有所不同, 这里针对SQLite数据库进行相应的拼接。
sourceProperty . setUrl ( SqliteConstant . URL_PREFIX + dbPath ) ;
// 设置驱动类名, 使用项目中定义的常量( SqliteConstant.DRIVER_CLASS_NAME) 来指定SQLite数据库的驱动类名称,
// 驱动类用于在Java程序与数据库之间建立通信连接, 告诉程序如何与特定类型的数据库进行交互。
sourceProperty . setDriverClassName ( SqliteConstant . DRIVER_CLASS_NAME ) ;
// 设置连接池名称, 通过调用DSNameUtil的getDSName方法, 传入微信用户ID( wxId) 和数据库名称( dbName) 作为参数, 生成一个唯一的连接池名称,
// 用于在动态路由数据源中区分不同的数据源,方便管理和使用。
sourceProperty . setPoolName ( DSNameUtil . getDSName ( wxId , dbName ) ) ;
// 通过Spring相关工具类( SpringUtil) 获取动态路由数据源对象, 该对象在应用中负责管理多个数据源, 根据不同的需求动态切换使用不同的数据源进行数据库操作。
DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtil . getBean ( DynamicRoutingDataSource . class ) ;
// 通过Spring相关工具类( SpringUtil) 获取默认数据源创建对象, 用于根据前面配置好的DataSourceProperty对象来创建实际的数据源对象( 如基于配置创建Druid数据源等) 。
DefaultDataSourceCreator dataSourceCreator = SpringUtil . getBean ( DefaultDataSourceCreator . class ) ;
// 使用数据源创建对象( dataSourceCreator) 根据配置好的数据源属性( sourceProperty) 创建实际的数据源对象,
// 该数据源对象就是可以与数据库进行交互的具体实例,包含了连接数据库的各种配置信息和连接资源等。
DataSource dataSource = dataSourceCreator . createDataSource ( sourceProperty ) ;
// 将创建好的数据源添加到动态路由数据源中, 通过动态路由数据源对象( dynamicRoutingDataSource) 的addDataSource方法,
// 传入连接池名称( sourceProperty.getPoolName()) 和数据源对象( dataSource) , 完成数据源的动态注册, 使得应用可以使用该数据源进行数据库操作。
dynamicRoutingDataSource . addDataSource ( sourceProperty . getPoolName ( ) , dataSource ) ;
}
}