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

东莞市瑞域塑胶制品有限公司 2026/09/09 17:33:39

代码

// 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、达梦等),成为异构数据库迁移的高效工具。

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

寿光网站建设莱州网站建设

YOLOv8 Flipping图像翻转增强策略(水平/垂直)在目标检测的实际项目中,一个常见的困境是:标注数据有限,模型却需要应对

2026/06/30 10:45:52

温州网站建设龙岩网站建设

Windows苹果触控板终极指南:快速上手mac-precision-touchpad驱动【免费下载链接】mac-precision-touchpadWindows Precision

2026/06/30 11:55:28

网站建设多少钱东营网站建设

你是否曾经为了分离音乐中的人声而反复调整参数?是否在嘈杂环境中录制的音频难以修复?现在,借助本地AI技术,这些问题都能轻松解决。OpenVINO

2026/06/30 13:27:36

网站建设策划方案银川网站建设

还在为城通网盘的龟速下载而头疼吗?每次看到那个缓慢的进度条,是不是感觉时间都停滞了?别担心,今天教你一套简单有效的方法,让城通网盘

2026/06/30 12:16:00

岳阳网站建设装饰网站建设

课程论文,作为大学生接触学术写作的最初形态,本应是探索知识、构建思维的训练场。但在现实中,它常常沦为时间、耐心和学术热情的“三重复合消耗战”。学生们面对着“选

2026/06/30 11:40:57

涪陵网站建设顺德网站建设

还在为手动拨打电话而烦恼?这款开源电话呼叫工具让你一键搞定批量呼叫需求!无论是学习自动化原理还是进行合法测试,这个基于Python的项目都能帮你轻松实现。【免

2026/06/30 10:46:52

牡丹江网站建设网站的建设公司

Fedora安装问题排查与使用指南1. 安装程序概述Fedora安装程序通常是一个可靠的工具,能很好地完成Linux系统的安装。但要享受其带来的便利,首先需确保能顺利启动它。安装过程中可能会遇到两个主

2026/06/30 12:43:02

网站建设流程网站建设空间

结合AI语音合成,Sonic可实现端到端全自动数字人视频生产在短视频内容爆炸式增长的今天,一个现实摆在创作者面前:观众对高质量、人格化表达的需求越来越高&#x

2026/06/30 13:46:37

怎么建设网站龙岗网站建设公司

CosyVoice3能否识别儿童声音?不同年龄层声纹适应性分析在智能语音助手走进千家万户的今天,越来越多家长开始期待一个能“像孩子一样说话”的AI角色——无论是为儿童故事配

2026/06/30 10:32:20