郑州网站建设陕西网站建设

泉州市泰晴文化传媒有限公司 2026/09/09 19:40:00

代码

// Copyright tang. All rights reserved. // https://gitee.com/inrgihc/dbswitch // // Use of this source code is governed by a BSD-style license // // Author: tang (inrgihc@126.com) // Date : 2020/1/2 // Location: beijing , china ///////////////////////////////////////////////////////////// package org.dromara.dbswitch.product.register; import org.dromara.dbswitch.core.annotation.Product; import org.dromara.dbswitch.common.consts.Constants; import org.dromara.dbswitch.common.type.ProductTypeEnum; import org.dromara.dbswitch.core.provider.ProductFactoryProvider; import org.dromara.dbswitch.core.provider.ProductProviderFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.ServiceConfigurationError; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Configuration; @Slf4j @Configuration @ConditionalOnClass(ProductProviderFactory.class) public class ProductRegisterAutoConfiguration implements InitializingBean, BeanClassLoaderAware { private static final Set<String> providers = new HashSet<>(); private ClassLoader classLoader; private int parseLine(BufferedReader reader, int lc, List<String> names) throws IOException, ServiceConfigurationError { String ln = reader.readLine(); if (ln == null) { return -1; } int ci = ln.indexOf('#'); if (ci >= 0) { ln = ln.substring(0, ci); } ln = ln.trim(); int n = ln.length(); if (n != 0) { if ((ln.indexOf(' ') >= 0) || (ln.indexOf('	') >= 0)) { log.error("Illegal configuration-file syntax: {}", ln); } int cp = ln.codePointAt(0); if (!Character.isJavaIdentifierStart(cp)) { log.error("Illegal provider-class name: {}", ln); } for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { cp = ln.codePointAt(i); if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) { log.error("Illegal provider-class name: {}", ln); } } if (!providers.contains(ln) && !names.contains(ln)) { names.add(ln); } } return lc + 1; } private List<String> parse(URL url) throws ServiceConfigurationError { InputStream in = null; BufferedReader reader = null; ArrayList<String> names = new ArrayList<>(); try { in = url.openStream(); reader = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(reader, lc, names)) >= 0) { } } catch (IOException x) { log.error("Error reading configuration file", x); } finally { try { if (reader != null) { reader.close(); } if (in != null) { in.close(); } } catch (IOException y) { log.error("Error closing configuration file", y); } } return names; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void afterPropertiesSet() throws Exception { log.info("Register database product now ..."); ClassLoader loader = (null != classLoader) ? classLoader : ProductProviderFactory.class.getClassLoader(); Enumeration<URL> resources = loader.getResources(Constants.SPI_FILE); while (resources.hasMoreElements()) { URL url = resources.nextElement(); providers.addAll(parse(url)); } int totalCount = 0; for (String className : providers) { Class<?> aClass = classLoader.loadClass(className); if (ProductFactoryProvider.class.isAssignableFrom(aClass)) { if (aClass.isAnnotationPresent(Product.class)) { Product annotation = aClass.getAnnotation(Product.class); ProductTypeEnum productType = annotation.value(); if (null != productType) { ProductProviderFactory.register(productType, className); ++totalCount; } } } } log.info("Finish to register total {} database product !", totalCount); } }

这段代码是dbswitch开源数据库同步工具的核心组件之一,负责在 Spring 容器启动时自动扫描并注册支持的数据库产品实现类。以下是详细解析:


🧩 一、核心功能概述

该类通过 Java 的 SPI(Service Provider Interface)机制动态加载数据库方言实现类,并将其注册到ProductProviderFactory工厂中,实现数据库产品的自动发现与注册。


🔍 二、关键代码解析

1.类定义与注解
@Slf4j @Configuration @ConditionalOnClass(ProductProviderFactory.class) public class ProductRegisterAutoConfiguration implements InitializingBean, BeanClassLoaderAware
  • @Configuration:声明为 Spring 配置类。
  • @ConditionalOnClass:仅当类路径存在ProductProviderFactory时生效(保障环境兼容性)。
  • InitializingBean:在 Bean 属性设置后触发afterPropertiesSet()初始化逻辑。
  • BeanClassLoaderAware:获取 Spring 容器的类加载器。
2.SPI 文件解析
private List<String> parse(URL url) { ... } private int parseLine(BufferedReader reader, int lc, List<String> names) { ... }
  • 作用:读取META-INF/services目录下的 SPI 配置文件(文件名由Constants.SPI_FILE定义)。
  • 流程
    1. 按行读取配置文件(如org.dromara.dbswitch.core.provider.ProductFactoryProvider)。
    2. 过滤注释(#开头)和非法类名(如包含空格或非 Java 标识符)。
    3. 将有效实现类全限定名存入providers集合。
3.数据库产品注册
@Override public void afterPropertiesSet() { // 获取类加载器 ClassLoader loader = (null != classLoader) ? classLoader : ProductProviderFactory.class.getClassLoader(); // 扫描所有 SPI 配置文件 Enumeration<URL> resources = loader.getResources(Constants.SPI_FILE); while (resources.hasMoreElements()) { providers.addAll(parse(resources.nextElement())); // 解析文件内容 } // 注册到工厂 int totalCount = 0; for (String className : providers) { Class<?> aClass = classLoader.loadClass(className); if (ProductFactoryProvider.class.isAssignableFrom(aClass) // 确保实现接口 && aClass.isAnnotationPresent(Product.class)) { // 检查 @Product 注解 Product annotation = aClass.getAnnotation(Product.class); ProductTypeEnum productType = annotation.value(); // 获取数据库类型(如 ORACLE、MYSQL) ProductProviderFactory.register(productType, className); // 注册到工厂 ++totalCount; } } log.info("Finish to register total {} database product !", totalCount); }

📦 三、技术要点总结

特性说明
SPI 机制通过META-INF/services文件实现数据库方言的可插拔扩展
动态注册利用 Spring 的InitializingBean在容器启动时完成注册
类型安全通过@Product注解绑定数据库类型枚举(如ProductTypeEnum.ORACLE
错误防护校验类名合法性,避免非法配置导致运行时异常

🌰 四、实际应用场景

假设新增达梦数据库支持:

  1. 实现接口
    @Product(ProductTypeEnum.DAMENG) public class DamengProductProvider implements ProductFactoryProvider { ... }
  2. SPI 配置
    META-INF/services/org.dromara.dbswitch.core.provider.ProductFactoryProvider文件中添加:
    org.dromara.dbswitch.dm.DamengProductProvider
  3. 自动注册
    Spring 启动时,该类会被扫描并注册到ProductProviderFactory,后续可通过ProductTypeEnum.DAMENG调用。

⚙️ 五、与 dbswitch 架构的关系

此组件是 dbswitch多数据库支持的基石,其设计体现了以下核心思想:

  1. 解耦:通过 SPI 分离接口与实现。
  2. 扩展性:新增数据库仅需添加实现类和 SPI 配置。
  3. 自动化:Spring 容器管理生命周期,避免手动注册。

通过这种设计,dbswitch 能灵活支持 20+ 种数据库(如 Oracle、MySQL、PostgreSQL、达梦等),成为异构数据库迁移的高效工具。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

网站制作建设邵阳网站建设

科哥是谁?CosyVoice3二次开发者,致力于推广AI普惠化在短视频与有声内容爆发的今天,一个现实问题摆在许多创作者面前:如何低成本地生成自然

2026/06/30 11:46:57

宜昌网站建设网站建设南京

基于考虑位错攀移的晶体塑性(CPFE)蠕变模拟在材料科学领域,对晶体材料在高温下的蠕变行为进行精准模拟至关重要。晶体塑性有限元(CPFE

2026/06/30 10:49:52

烟台网站建设旅游网站建设方案

第一章:Dify React 19.2.3 安全更新的紧迫性随着前端生态的快速发展,React 框架的版本迭代频繁引入新特性与安全补丁。Dify 基于 React 19.2

2026/06/30 13:47:37

青岛网站建设公司机械网站建设

语音信号降噪(语音转.mat—滤波—.mat转降噪后语音,MATLAB),程序已调通可直接运行。最近在研究语音信号处理,其中降噪是一个关键环节。今天就来跟大家

2026/06/30 13:23:05

陕西网站建设网站建设知识

当你在使用ComfyUI-Manager时遇到"此操作在当前安全级别下不被允许"的提示,这意味着系统的安全配置限制了某些功能的使用。ComfyUI安全级别机制旨在保护你的

2026/06/30 10:04:48

西安网站建设长沙网站建设

3步搞定无人机航拍地图:从零开始的几何变换实战指南【免费下载链接】korniaGeometric Computer Vision Library for AI项目地址: https://

2026/06/30 12:49:03

电器网站建设布吉网站建设

【实战复盘】告别C盘爆红:从0到1构建Windows 11智能垃圾清理系统摘要:Windows 11虽然引入了存储感知,但在面对深层开发缓存、老旧更新残留及特

2026/06/30 11:38:26

网站建设合同鞍山网站建设

还在忍受百度网盘几十KB的下载速度吗?面对大文件传输时的漫长等待,是否让你倍感焦虑?这款百度网盘直链解析工具将彻底改变你的下载体验,通过提取真实

2026/06/30 10:33:20

郴州网站建设南京网站建设公司

SSH远程开发配置指南:基于Miniconda-Python3.11的高效AI工作流在高校实验室里,一个学生正对着自己轻薄本上“CUDA out of memory”的报错

2026/06/30 13:54:38

宜昌网站建设东莞南城网站建设

你是否曾经幻想过拥有一个能够理解你、陪伴你的智能虚拟伙伴?现在,这个梦想可以通过Open-LLM-VTuber项目轻松实现。作为一个完全离线运行的AI虚拟主播解决方案&#x

2026/06/30 12:39:32