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

中国租赁网 2026/09/09 17:48:37

代码

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

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

北海网站建设网站建设代理

Internet Explorer 9 全方位使用指南在当今数字化的时代,浏览器已经成为我们生活中不可或缺的工具。Internet Explorer 9 以其快速的浏览体验和强大的功能,为用户提供了便

2026/06/30 11:00:53

网站制作建设摄影网站建设

面对堆积如山的报表、散落各处的数据,以及瞬息万变的市场,施工企业的管理者们常常感到力不从心:项目成本究竟是否可控?资金流是否安全?经营风险藏在哪里?选择一款真正懂业务、能落地的数字化管理系统,已不再是

2026/06/30 10:51:52

嘉兴网站建设东莞南城网站建设

技术实践观察地址:电子木鱼摘要:电子木鱼这类看似简单的应用,是人机交互(HCI)领域中关于**“微交互设计”和“即时奖励机制”的典

2026/06/30 10:36:21

衡水网站建设网站建设品牌

终极指南:如何使用vendor-reset模块轻松重置AMD显卡【免费下载链接】vendor-resetLinux kernel vendor specific hardware res

2026/06/30 13:59:38

睢宁网站建设网站建设的方案

VoiceFixer语音修复工具:让受损音频重获新生的终极指南【免费下载链接】voicefixerGeneral Speech Restoration项目地址: https://gitc

2026/06/30 13:25:35

丹阳网站建设湘潭网站建设

目录摘要开发技术核心代码参考示例1.建立用户稀疏矩阵,用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度总结源码文档获取/同行可拿货,招校园代理 :文章底部

2026/06/30 11:52:58

建设局网站宜昌网站建设

PetaPoco终极指南:5分钟掌握.NET微型ORM配置技巧【免费下载链接】PetaPoco项目地址: https://gitcode.com/gh_mirrors/pe/PetaPo

2026/06/30 12:21:30

西安网站建设摄影网站建设

OCAuxiliaryTools完整使用指南:3步搞定OpenCore配置【免费下载链接】OCAuxiliaryToolsCross-platform GUI management to

2026/06/30 12:30:02

如何建设网站团购网站建设

技术趋势总览2024年技术发展聚焦人工智能、云计算、边缘计算、量子计算等领域的深度融合,跨学科创新成为关键驱动力。人工智能与机器学习生成式AI在代码自动生成、测试用例编写等场景加速落地&

2026/06/30 13:52:37