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

无锡市赣发物流有限公司 2026/09/09 19:24:51

代码

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

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

网站建设教程惠州网站建设

PathOfBuilding终极指南:从零开始快速掌握流放之路Build规划【免费下载链接】PathOfBuildingOffline build planner for Path of

2026/06/30 13:14:04

温州网站建设建设网站建设

PyTorch设备(Device)管理:CPU与GPU之间移动张量在现代深度学习开发中,一个看似简单却极易出错的操作——“把张量放到GPU上”—

2026/06/30 13:56:08

网站建设书十堰网站建设

快速体验打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容:创建一个Python函数学习助手,能够根据用户输入的函数名自动

2026/06/30 10:48:52

银川网站建设商业网站建设案例课程

智慧树插件仿写文章创作规范【免费下载链接】zhihuishu智慧树刷课插件,自动播放下一集、1.5倍速度、无声项目地址: https://gitcode.com/gh_mirrors/z

2026/06/30 13:23:35

济宁网站建设邢台网站建设

Diskinfo检测SSD寿命:确保GPU服务器长期稳定运行在大规模AI训练任务中,一次意外的存储故障可能意味着数天甚至数周的计算成果付诸东流。你有没有遇到过这样的场景&#

2026/06/30 11:37:56

开县网站建设天津网站建设公司

强烈安利10个AI论文平台,MBA毕业论文写作必备!AI 工具如何重塑论文写作的未来在当今快速发展的学术环境中,MBA 学生面临着越来越高的论文写作要求。无论

2026/06/30 12:00:58

房产网站建设网站建设广告

OrcaSlicer 3D打印参数优化指南:从新手到专家的完整路径【免费下载链接】OrcaSlicerG-code generator for 3D printers (Bambu, P

2026/06/30 10:47:52

php网站建设宜兴网站建设

还在为繁重的职教平台课程任务而烦恼吗?hcqHome智能刷课助手通过自动化技术,帮你轻松应对职教云、智慧职教、资源库三大平台的课程学习需求。这款开源工具不仅能够大幅节省你的

2026/06/30 14:11:39