This is an automated email from the ASF dual-hosted git repository.

jimin pushed a commit to branch docusaurus
in repository https://gitbox.apache.org/repos/asf/incubator-seata-website.git


The following commit(s) were added to refs/heads/docusaurus by this push:
     new e61a786a57e doc: add a blog about config parse logic (#1014)
e61a786a57e is described below

commit e61a786a57e4a4d9fd9e92b8c8f1fa7c3d47aa08
Author: xiaoyu <[email protected]>
AuthorDate: Wed Sep 3 00:14:49 2025 +0800

    doc: add a blog about config parse logic (#1014)
---
 ...seata-loads-configurations\342\200\223part1.md" |   1 +
 ...seata-loads-configurations\342\200\223part1.md" | 356 +++++++++++++++++++++
 ...seata-loads-configurations\342\200\223part1.md" | 340 ++++++++++++++++++++
 3 files changed, 697 insertions(+)

diff --git 
"a/blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md" 
"b/blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
new file mode 100644
index 00000000000..8f9293a8d53
--- /dev/null
+++ 
"b/blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
@@ -0,0 +1 @@
+Placeholder. DO NOT DELETE.
diff --git 
"a/i18n/en/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
 
"b/i18n/en/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
new file mode 100644
index 00000000000..2d1d2d846ca
--- /dev/null
+++ 
"b/i18n/en/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
@@ -0,0 +1,356 @@
+---
+title: Understanding How Seata Loads Configuration Information Part One
+keywords: [Seata, Config]
+description: In distributed systems, configuration management is crucial. This 
article will guide you step by step from scratch to understand how Seata loads 
and manages configuration information. This is the first part of a series, 
focusing on the most basic configuration loading process to lay a foundation 
for further in-depth source code analysis.
+author: Yu Zhang
+date: 2025-08-24
+---
+
+## Introduction
+In internet applications, system configuration information is the cornerstone 
of software operation and the core on which upper-layer business logic relies. 
Therefore, configuration information is often loaded at an early stage of the 
project startup lifecycle to ensure smooth operation of subsequent business 
processes.
+Seata, as a distributed transaction coordinator, follows this same principle. 
Seata's configuration loading process can be roughly divided into two stages:
+
+1. **Local configuration file loading**
+2. **Loading remote configuration from a configuration center based on the 
local configuration**
+
+This article focuses on the first stage—how Seata reads and initializes 
relevant configuration content from local configuration files. As for the 
second stage, which involves connecting to a configuration center and 
implementing dynamic configuration refresh, we will cover it in detail in the 
next article. Key topics in this article include:
+
++ At **which stage** Seata loads configuration;
++ The **complete process** of configuration loading;
++ The **dynamic update mechanism** of configuration.
+
+---
+
+## I. Loading Steps
+
+### 1.1 Entry Point
+In Seata, both the `TC` side and the `Client` side depend on the 
`autoconfigure-core` module, which defines a core class `SeataPropertiesLoader` 
implementing the 
`ApplicationContextInitializer<ConfigurableApplicationContext>` interface.
+Classes implementing this interface are executed **before Spring loads bean 
definitions**, making it ideal for adjusting the `Environment`, registering 
additional `PropertySources`, or modifying configurations at this stage.
+Seata leverages this feature to complete its initial configuration loading 
early in the project startup process.
+Here’s the core implementation of `SeataPropertiesLoader`:
+
+```java
+// Specified in spring.factories with the highest loading priority
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class SeataPropertiesLoader implements 
ApplicationContextInitializer<ConfigurableApplicationContext> {
+
+    List<String> prefixList = Arrays.asList(FILE_ROOT_PREFIX_CONFIG, 
FILE_ROOT_PREFIX_REGISTRY, SERVER_PREFIX,
+        STORE_PREFIX, METRICS_PREFIX, TRANSPORT_PREFIX);
+
+    @Override
+    public void initialize(ConfigurableApplicationContext applicationContext) {
+        // ConfigurableEnvironment is a subinterface of Environment. It allows 
injecting configurations dynamically during application context initialization.
+        ConfigurableEnvironment environment = 
applicationContext.getEnvironment();
+
+       // Use the configuration factory to obtain the instance object 
corresponding to the initial registry configuration file.
+        // Additionally, before executing this line for the first time, the 
static block initialization logic will be triggered. The core configuration 
initialization logic is here and will be explained in detail below.
+        FileConfiguration configuration = 
ConfigurationFactory.getOriginFileInstanceRegistry();
+        FileConfig fileConfig = configuration.getFileConfig();
+        Map<String, Object> configs = fileConfig.getAllConfig();
+        if (CollectionUtils.isNotEmpty(configs)) {
+            Optional<FileConfiguration> originFileInstance = 
ConfigurationFactory.getOriginFileInstance();
+            originFileInstance
+                .ifPresent(fileConfiguration -> 
configs.putAll(fileConfiguration.getFileConfig().getAllConfig()));
+            Properties properties = new Properties();
+            configs.forEach((k, v) -> {
+                if (v instanceof String) {
+                    if (StringUtils.isEmpty((String)v)) {
+                        return;
+                    }
+                }
+                // Only keep configurations starting with fixed prefixes
+                if (prefixList.stream().anyMatch(k::startsWith)) {
+                    properties.put(SEATA_FILE_PREFIX_ROOT_CONFIG + k, v);
+                }
+            });
+            // Wrap as a PropertiesPropertySource and dynamically add it to 
Spring Boot's Environment.
+            environment.getPropertySources().addLast(new 
PropertiesPropertySource("seataOldConfig", properties));
+        }
+        // Load by priority
+        System.setProperty("sessionMode", 
StoreConfig.getSessionMode().getName());
+        System.setProperty("lockMode", StoreConfig.getLockMode().getName());
+    }
+
+}
+````
+
+Summary:
+
+* `SeataPropertiesLoader` executes before Spring Bean loading;
+* Retrieves initial configuration via `ConfigurationFactory`;
+* Converts matching configurations into a `PropertiesPropertySource` and 
dynamically injects them into `Spring Environment`;
+* Sets some global system properties (`sessionMode`, `lockMode`).
+
+---
+
+### 1.2 `ConfigurationFactory` Initialization Logic
+
+`ConfigurationFactory` is the core factory class for loading Seata 
configurations. Its static block defines three key steps:
+
+```java
+static {
+    initOriginConfiguration();
+    load();
+    maybeNeedOriginFileInstance();
+}
+```
+
+These three methods are interconnected, forming the complete Seata 
configuration loading process. Let's analyze them one by one.
+
+---
+
+#### 1.2.1 `initOriginConfiguration()`
+
+> This code is the **key logic of Seata configuration system initialization**. 
It primarily locates and loads the `registry.conf` (or environment-specific 
`registry-{env}.conf`) configuration file and constructs a `FileConfiguration` 
instance.
+
+```java
+private static void initOriginConfiguration() {
+       // First, get seata.config.name (SEATA_CONFIG_NAME) from startup 
parameters or environment variables
+       String seataConfigName = 
System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
+       if (seataConfigName == null) {
+               seataConfigName = System.getenv(ENV_SEATA_CONFIG_NAME);
+       }
+       // If neither is set, default to "registry"
+       if (seataConfigName == null) {
+               seataConfigName = REGISTRY_CONF_DEFAULT;
+       }
+
+       // If "env" is explicitly specified, append in the format registry-{env}
+       String envValue = System.getProperty(ENV_PROPERTY_KEY);
+       if (envValue == null) {
+               envValue = System.getenv(ENV_SYSTEM_KEY);
+       }
+       seataConfigName = envValue == null ? seataConfigName : seataConfigName 
+ "-" + envValue;
+       // Assign the FileConfiguration instance to the 
ORIGIN_FILE_INSTANCE_REGISTRY property of the factory
+       ORIGIN_FILE_INSTANCE_REGISTRY = new FileConfiguration(seataConfigName, 
false);
+}
+```
+
+Internal logic of `FileConfiguration`:
+
+```java
+public FileConfiguration(String name, boolean allowDynamicRefresh) {
+       // Try to find a configuration file starting with "registry" in .conf, 
.properties, or .yml format locally. If found, construct the corresponding File 
object.
+       // The latest version of Seata supports application.yml(properties) 
style files, so if not explicitly specified, this will hit file == null.
+       File file = getConfigFile(name);
+       if (file == null) {
+               targetFilePath = null;
+               // Load a default SimpleFileConfig instance via SPI, which 
internally relies on the Typesafe library to initialize system properties
+               fileConfig = FileConfigFactory.load();
+               this.allowDynamicRefresh = false;
+       } else {
+               targetFilePath = file.getPath();
+               // Load the configuration file based on its type. The Typesafe 
library loads both custom config files and system environment variables, JVM 
properties, etc.
+               fileConfig = FileConfigFactory.load(file, name);
+               targetFileLastModified = new 
File(targetFilePath).lastModified();
+               this.allowDynamicRefresh = allowDynamicRefresh;
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("The file name of the operation is {}", 
name);
+               }
+       }
+       this.name = name;
+       // Create a thread pool for configuration operations
+       configOperateExecutor = new ThreadPoolExecutor(
+               CORE_CONFIG_OPERATE_THREAD,
+               MAX_CONFIG_OPERATE_THREAD,
+               Integer.MAX_VALUE,
+               TimeUnit.MILLISECONDS,
+               new LinkedBlockingQueue<>(),
+               new NamedThreadFactory("configOperate", 
MAX_CONFIG_OPERATE_THREAD));
+}
+```
+
+1. Look for `registry.conf`, `registry.properties`, `registry.yml`, etc. 
locally;
+2. If found, parse it using `Typesafe Config`;
+3. If not found, use `SimpleFileConfig` to load JVM system properties, 
environment variables, etc.;
+4. Initialize a thread pool for configuration refresh operations.
+
+Conclusion:
+
+`ORIGIN_FILE_INSTANCE_REGISTRY` acts as Seata’s “original configuration 
center” at startup, storing the initially loaded configuration data.
+
+---
+
+#### 1.2.2 `load()`
+
+On top of the original configuration, `load()` enhances configuration 
retrieval via SPI, supporting additional fallback logic:
+
+```java
+private static void load() {
+    // Get the base configuration object from ORIGIN_FILE_INSTANCE_REGISTRY 
(default from local file, e.g., registry.conf)
+    Configuration configuration = ORIGIN_FILE_INSTANCE_REGISTRY;
+    Configuration extConfiguration = null;
+    try {
+        // Attempt to load an ExtConfigurationProvider implementation via 
Seata's SPI mechanism and obtain an enhanced configuration
+        // This step is critical and will be explained below
+        extConfiguration =
+        
EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
+        if (LOGGER.isInfoEnabled()) {
+            LOGGER.info(
+                "load Configuration from :{}",
+                extConfiguration == null
+                ? configuration.getClass().getSimpleName()
+                : extConfiguration.getClass().getSimpleName());
+        }
+    } catch (EnhancedServiceNotFoundException e) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("failed to load extConfiguration: {}", 
e.getMessage(), e);
+        }
+    } catch (Exception e) {
+        LOGGER.error("failed to load extConfiguration: {}", e.getMessage(), e);
+    }
+    // If no extension is loaded, use the original configuration. Assign 
result to CURRENT_FILE_INSTANCE for all future configuration access.
+    CURRENT_FILE_INSTANCE = extConfiguration == null ? configuration : 
extConfiguration;
+}
+```
+
+Log output shows that `extConfiguration` is a proxy object:
+
+```latex
+load Configuration from :FileConfiguration$$EnhancerByCGLIB$$6e15d955
+```
+
+---
+
+Now, let’s examine the `provide` method in `ExtConfigurationProvider`:
+
+This method essentially acts as a configuration enhancement layer. It uses 
CGLIB to proxy the original configuration object, intercepting configuration 
reads to implement multi-level fallback: **System > Spring > Method default > 
Property default > Original proxied object**.
+
+```java
+@Override
+public Configuration provide(Configuration originalConfiguration) {
+    return (Configuration) Enhancer.create(
+        originalConfiguration.getClass(),
+        (MethodInterceptor) (proxy, method, args, methodProxy) -> {
+            if (method.getName().startsWith(INTERCEPT_METHOD_PREFIX) && 
args.length > 0) {
+                Object result;
+                String rawDataId = (String) args[0];
+                Class<?> dataType = 
ReflectionUtil.getWrappedClass(method.getReturnType());
+
+                // 1. Get from system properties
+                result = originalConfiguration.getConfigFromSys(rawDataId);
+
+                if (result == null) {
+                    String dataId = convertDataId(rawDataId);
+
+                    // 2. Get from Spring Boot Environment
+                    result = getConfigFromEnvironment(dataId, dataType);
+                    if (result != null) {
+                        return result;
+                    }
+
+                    // 3. Use default value passed during method call
+                    if (args.length > 1) {
+                        result = args[1];
+                        if (result != null && 
dataType.isAssignableFrom(result.getClass())) {
+                            return result;
+                        }
+                        result = null;
+                    }
+
+                    // 4. Get default value from Properties object, 
corresponding to configuration classes initialized by 
SeataServerEnvironmentPostProcessor in auto-config module
+                    try {
+                        result = getDefaultValueFromPropertyObject(dataId);
+                    } catch (Throwable t) {
+                        LOGGER.error("Get config '{}' default value from the 
property object failed:", dataId, t);
+                    }
+                }
+
+                // Type conversion
+                if (result != null) {
+                    if (dataType.isAssignableFrom(result.getClass())) {
+                        return result;
+                    }
+                    return this.convertType(result, dataType);
+                }
+            }
+
+            return method.invoke(originalConfiguration, args);
+        });
+}
+```
+
+In summary, every time a configuration value is read, the system dynamically 
checks multiple sources in priority order. At this point, two key 
`ConfigurationFactory` variables are fully initialized:
+
+```java
+public static volatile Configuration CURRENT_FILE_INSTANCE;
+
+public static volatile FileConfiguration ORIGIN_FILE_INSTANCE_REGISTRY;
+```
+
+Finally:
+
+`CURRENT_FILE_INSTANCE` becomes the unified entry point for all subsequent 
configuration access.
+
+---
+
+#### 1.2.3 `maybeNeedOriginFileInstance()`
+
+This method initializes `file.conf` configuration dynamically based on the 
configuration type.
+
+```java
+private static void maybeNeedOriginFileInstance() {
+    if (ConfigType.File.name().equalsIgnoreCase(getConfigType())) {
+        String pathDataId = String.join(
+            ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
+            ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY
+        );
+        String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
+        ORIGIN_FILE_INSTANCE = new FileConfiguration(name);
+    }
+}
+```
+
+Here, `getConfigType()` uses `CURRENT_FILE_INSTANCE` to read `config.type`, 
determining the configuration source (e.g., `file`, `nacos`, `apollo`, etc.).
+
+Summary:
+
+This is a lazy-loading or on-demand initialization logic. Only if the 
configuration type is `file` will it load `file.conf` as additional 
configuration. (The `file.conf` file is often used to specify basic properties 
when using file mode as a configuration center, and this is heavily used in 
Seata’s internal integration tests.)
+
+Let's briefly examine `getConfigType`. At this point, `CURRENT_FILE_INSTANCE` 
handles the logic, so fetching `config.type` will follow the proxy logic above. 
`config.type` must be specified in local configuration to determine which 
configuration center to use.
+
+```java
+private static String getConfigType() {
+        String configTypeName = 
CURRENT_FILE_INSTANCE.getConfig(ConfigurationKeys.FILE_ROOT_CONFIG
+                + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
+                + ConfigurationKeys.FILE_ROOT_TYPE);
+        if (StringUtils.isBlank(configTypeName)) {
+            throw new NotSupportYetException("config type can not be null");
+        }
+        return configTypeName;
+    }
+```
+
+---
+
+## II. Conclusion
+
+Through this analysis, we have sorted out the **first stage of Seata 
configuration loading—local configuration loading process**:
+
+* `SeataPropertiesLoader` acts as the entry point, intervening before Spring 
Bean initialization to dynamically inject configurations;
+* `ConfigurationFactory`, as the core factory, completes three key steps 
during static initialization:
+
+  1. Load local configuration files and construct 
`ORIGIN_FILE_INSTANCE_REGISTRY`;
+  2. Enhance configuration retrieval via SPI and produce a unified entry 
`CURRENT_FILE_INSTANCE`;
+  3. Determine whether to load additional configs like `file.conf` based on 
`config.type`.
+* This stage ensures that Seata can obtain complete local configuration at 
startup, laying the foundation for initializing subsequent components.
+
+However, Seata's configuration system is not limited to this. In production 
environments, most users integrate with configuration centers (such as Nacos, 
Apollo, ZooKeeper, etc.) for centralized configuration management and dynamic 
refresh. The **loading and dynamic refresh mechanism of configuration centers** 
is the second important stage of Seata's configuration system.
+
+Due to space limitations, this article only focuses on the “local 
configuration loading” logic. In the next article, we will delve into the 
second stage, including:
+
+* How Seata connects to a remote configuration center based on local 
configuration;
+* The principles and thread model of dynamic refresh mechanism;
+* How to gracefully implement real-time configuration changes.
+
+Stay tuned for the next article: **"Detailed Analysis of Seata Configuration 
Center Loading and Dynamic Refresh Mechanism"**.
+
+
+
+
+
+
+
+
+
+
diff --git 
"a/i18n/zh-cn/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
 
"b/i18n/zh-cn/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
new file mode 100644
index 00000000000..7eb4d973f14
--- /dev/null
+++ 
"b/i18n/zh-cn/docusaurus-plugin-content-blog/beginner-guide-to-how-seata-loads-configurations\342\200\223part1.md"
@@ -0,0 +1,340 @@
+---
+title: 从0到1了解Seata是如何加载配置信息的-01
+keywords: [Seata, Config]
+description: 在分布式系统中,配置管理至关重要。本文将带你从零开始,逐步了解 Seata 
是如何加载和管理配置信息的。本篇是系列文章的第一部分,聚焦最基础的配置加载流程,为后续深入源码分析打下基础。
+author: 张宇|波克科技集团
+date: 2025-08-24
+---
+
+## 引言
+在互联网应用中,系统配置信息是软件运行的基石,也是上层业务逻辑赖以运行的核心。因此,项目启动生命周期的早期阶段往往就会加载配置信息,以便后续的业务流程顺利运行。
+Seata 作为一款分布式事务协调器,同样遵循这一规律。在 Seata 的配置加载流程中,可以大致分为两个阶段:
+
+1. **本地配置文件加载**
+2. **基于本地配置,加载配置中心的远程配置**
+
+本文主要聚焦第一阶段—:也就是 Seata 
如何从本地配置文件中读取和初始化相关配置内容。至于第二阶段,也就是对接配置中心和实现配置动态刷新这一块,我们会在下一篇文章中详细展开。重点讲解:
+
++ Seata 在**哪个阶段**加载配置;
++ 配置加载的**完整流程**;
++ 配置的**动态更新机制**。
+
+## 一、加载步骤
+### 1.1 加载入口
+在Seata中,无论是 `TC` 端还是`Client`端,都依赖了`autoconfigure-core`模块,该模块中定义了一个核心类 
`SeataPropertiesLoader`,它实现了 
`ApplicationContextInitializer<ConfigurableApplicationContext>` 接口。
+
+实现这个接口的类会在 **Spring 加载 Bean 定义之前**执行,非常适合在此阶段对 `Environment` 进行调整、注册额外的 
`PropertySource` 或修改配置。
+Seata 正是利用这一特性,在项目启动早期完成了配置的初始加载。
+
+来看 `SeataPropertiesLoader` 的核心实现:
+
+```java
+// 在spring.factories中指明了当前实现类,且加载优先级为最高
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class SeataPropertiesLoader implements 
ApplicationContextInitializer<ConfigurableApplicationContext> {
+
+    List<String> prefixList = Arrays.asList(FILE_ROOT_PREFIX_CONFIG, 
FILE_ROOT_PREFIX_REGISTRY, SERVER_PREFIX,
+        STORE_PREFIX, METRICS_PREFIX, TRANSPORT_PREFIX);
+
+    @Override
+    public void initialize(ConfigurableApplicationContext applicationContext) {
+        // ConfigurableEnvironment是Environment的子接口,可以在应用上下文初始化的时候,动态注入配置。
+        ConfigurableEnvironment environment = 
applicationContext.getEnvironment();
+
+    ˙  // 通过配置工厂类,获取初始registry配置文件的对应的实例对象。
+        // 除此之外,这行代码第一次执行前会触发静态代码块内相关逻辑的初始化,核心配置逻辑的初始化都在里面,下文会详细介绍
+        FileConfiguration configuration = 
ConfigurationFactory.getOriginFileInstanceRegistry();
+        FileConfig fileConfig = configuration.getFileConfig();
+        Map<String, Object> configs = fileConfig.getAllConfig();
+        if (CollectionUtils.isNotEmpty(configs)) {
+            Optional<FileConfiguration> originFileInstance = 
ConfigurationFactory.getOriginFileInstance();
+            originFileInstance
+                .ifPresent(fileConfiguration -> 
configs.putAll(fileConfiguration.getFileConfig().getAllConfig()));
+            Properties properties = new Properties();
+            configs.forEach((k, v) -> {
+                if (v instanceof String) {
+                    if (StringUtils.isEmpty((String)v)) {
+                        return;
+                    }
+                }
+                // 此处仅保留以固定前缀开头的配置
+                if (prefixList.stream().anyMatch(k::startsWith)) {
+                    properties.put(SEATA_FILE_PREFIX_ROOT_CONFIG + k, v);
+                }
+            });
+            // 封装成一个 PropertiesPropertySource,然后动态加到 Spring Boot 的 Environment 
里。
+            environment.getPropertySources().addLast(new 
PropertiesPropertySource("seataOldConfig", properties));
+        }
+        // Load by priority
+        System.setProperty("sessionMode", 
StoreConfig.getSessionMode().getName());
+        System.setProperty("lockMode", StoreConfig.getLockMode().getName());
+    }
+
+}
+```
+
+总结:
+
++ `SeataPropertiesLoader` 在 Spring Bean 加载前执行;
++ 通过 `ConfigurationFactory` 获取初始配置;
++ 将符合条件的配置转为 `PropertiesPropertySource`,动态注入 `Spring Environment`;
++ 同时设置一些全局系统属性(`sessionMode`、`lockMode`)。
+
+### 1.2 `ConfigurationFactory` 初始化逻辑
+`ConfigurationFactory` 是 Seata 配置加载的核心工厂类,它的静态代码块定义了三大步骤:
+
+```java
+static {
+    initOriginConfiguration();
+    load();
+    maybeNeedOriginFileInstance();
+}
+```
+
+这三个方法互相关联,串起了 Seata 配置加载的完整流程,接下来让我们逐一对其进行分析
+
+#### 1.2.1 `initOriginConfiguration()`
+> 这段代码是 **Seata 配置系统初始化的关键逻辑**,主要负责找到并加载 `registry.conf`(或者带环境后缀的 
`registry-{env}.conf`)配置文件,并用它构造一个 `FileConfiguration` 实例。
+>
+
+```java
+private static void initOriginConfiguration() {
+       // 这里会先从启动参数或者环境变量里面去取seata.config.name(SEATA_CONFIG_NAME)
+       String seataConfigName = 
System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
+       if (seataConfigName == null) {
+               seataConfigName = System.getenv(ENV_SEATA_CONFIG_NAME);
+       }
+       // 前者没有配置属性则取默认值registry
+       if (seataConfigName == null) {
+               seataConfigName = REGISTRY_CONF_DEFAULT;
+       }
+
+       // 这里如果特地指定了env的话,那么下一步会拼接registry-{env}这种格式
+       String envValue = System.getProperty(ENV_PROPERTY_KEY);
+       if (envValue == null) {
+               envValue = System.getenv(ENV_SYSTEM_KEY);
+       }
+       seataConfigName = envValue == null ? seataConfigName : seataConfigName 
+ "-" + envValue;
+       // 最终将FileConfuration的对象实例赋值给工厂类下面的ORIGIN_FILE_INSTANCE_REGISTRY属性
+       ORIGIN_FILE_INSTANCE_REGISTRY = new FileConfiguration(seataConfigName, 
false);
+}
+```
+
+`FileConfiguration` 内部逻辑:
+
+```java
+
+public FileConfiguration(String name, boolean allowDynamicRefresh) {
+       // 
会在本地尝试获取以registry开头的.conf、.properties、.yml格式的配置文件,如果存在则会构建对应的File对象并返回
+       // 
目前新版本的seata,已经支持application.yml(proeprties)风格的配置文件,所以对于新版本来说(如果没有显式配置registry开头的配置文件),命中的是file
 == null的逻辑
+       File file = getConfigFile(name);
+       if (file == null) {
+               targetFilePath = null;
+               // 
会通过SPI底层加载一个默认的SimpleFileConfig对象,该对象内部依赖了Typesafe三方库用于初始化一些系统属性
+               fileConfig = FileConfigFactory.load();
+               this.allowDynamicRefresh = false;
+       } else {
+               targetFilePath = file.getPath();
+               // 会根据registry文件的类型加载对应的文件配置对象 
Typesafe三方库不仅会加载我们自定义的配置文件还会去加载系统环境变量、jvm级别的属性等等
+               fileConfig = FileConfigFactory.load(file, name);
+               targetFileLastModified = new 
File(targetFilePath).lastModified();
+               this.allowDynamicRefresh = allowDynamicRefresh;
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("The file name of the operation is {}", 
name);
+               }
+       }
+       this.name = name;
+       // 创建配置操作线程池
+       configOperateExecutor = new ThreadPoolExecutor(
+               CORE_CONFIG_OPERATE_THREAD,
+               MAX_CONFIG_OPERATE_THREAD,
+               Integer.MAX_VALUE,
+               TimeUnit.MILLISECONDS,
+               new LinkedBlockingQueue<>(),
+               new NamedThreadFactory("configOperate", 
MAX_CONFIG_OPERATE_THREAD));
+}
+```
+
+1. 在本地查找 `registry.conf`、`registry.properties`、`registry.yml` 等文件;
+2. 如果找到文件,则用 `Typesafe Config` 解析;
+3. 如果找不到文件,则使用 `SimpleFileConfig`,加载 JVM 系统属性、环境变量等;
+4. 初始化一个线程池,用于后续配置刷新。
+
+结论:
+
+`ORIGIN_FILE_INSTANCE_REGISTRY` 是 Seata 启动时的“原始配置中心”,保存了最初加载的配置数据。
+
+#### 1.2.2 `load()`
+在原始配置基础上,`load()` 会通过 SPI 机制增强配置获取能力,支持更多兜底逻辑:
+
+```java
+private static void load() {
+    // 从 ORIGIN_FILE_INSTANCE_REGISTRY 拿到基础配置对象(默认基于本地文件,如 registry.conf)
+    Configuration configuration = ORIGIN_FILE_INSTANCE_REGISTRY;
+    Configuration extConfiguration = null;
+    try {
+        // 尝试通过 EnhancedServiceLoader(Seata 的 SPI 机制)去加载一个 
ExtConfigurationProvider 扩展实现并获得一个增强后的configuration
+        // 这一步非常的关键,下文会详细介绍
+        extConfiguration =
+        
EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
+        if (LOGGER.isInfoEnabled()) {
+            LOGGER.info(
+                "load Configuration from :{}",
+                extConfiguration == null
+                ? configuration.getClass().getSimpleName()
+                : extConfiguration.getClass().getSimpleName());
+        }
+    } catch (EnhancedServiceNotFoundException e) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("failed to load extConfiguration: {}", 
e.getMessage(), e);
+        }
+    } catch (Exception e) {
+        LOGGER.error("failed to load extConfiguration: {}", e.getMessage(), e);
+    }
+    // 如果加载不到扩展,就继续用原始的 configuration,最终把结果赋值给 
CURRENT_FILE_INSTANCE,后续配置读取都会走它。
+    CURRENT_FILE_INSTANCE = extConfiguration == null ? configuration : 
extConfiguration;
+}
+```
+
+通过日志可以看到:extConfiguration是一个代理对象
+
+```latex
+load Configuration from :FileConfiguration$$EnhancerByCGLIB$$6e15d955
+
+```
+
+
+
+接下来我们再细看ExtConfigurationProvider的provide方法:
+
+这段方法本质上是一个配置增强层,通过cglib代理了原始的configuration对象,拦截配置读取调用,实现 优先查系统 -> Spring -> 
方法默认 -> Property 默认 -> 原始被代理对象 的多层兜底逻辑。
+
+```java
+@Override
+public Configuration provide(Configuration originalConfiguration) {
+    return (Configuration) Enhancer.create(
+        originalConfiguration.getClass(),
+        (MethodInterceptor) (proxy, method, args, methodProxy) -> {
+            if (method.getName().startsWith(INTERCEPT_METHOD_PREFIX) && 
args.length > 0) {
+                Object result;
+                String rawDataId = (String) args[0];
+                Class<?> dataType = 
ReflectionUtil.getWrappedClass(method.getReturnType());
+
+                // 1. 从系统属性获取
+                result = originalConfiguration.getConfigFromSys(rawDataId);
+
+                if (result == null) {
+                    String dataId = convertDataId(rawDataId);
+
+                    // 2. 从 Spring Boot Environment 获取
+                    result = getConfigFromEnvironment(dataId, dataType);
+                    if (result != null) {
+                        return result;
+                    }
+
+                    // 3. 方法调用时传入的默认值
+                    if (args.length > 1) {
+                        result = args[1];
+                        if (result != null && 
dataType.isAssignableFrom(result.getClass())) {
+                            return result;
+                        }
+                        result = null;
+                    }
+
+                    // 4. 从 Properties 
对象里拿默认值。这里对应的是在auto-config模块中,SeataServerEnvironmentPostProcessor初始化的一些配置类
+                    try {
+                        result = getDefaultValueFromPropertyObject(dataId);
+                    } catch (Throwable t) {
+                        LOGGER.error("Get config '{}' default value from the 
property object failed:", dataId, t);
+                    }
+                }
+
+                // 类型转换
+                if (result != null) {
+                    if (dataType.isAssignableFrom(result.getClass())) {
+                        return result;
+                    }
+                    return this.convertType(result, dataType);
+                }
+            }
+
+            return method.invoke(originalConfiguration, args);
+        });
+}
+
+```
+
+总结下来,每次读取配置时,都会动态地按照优先级查找不同来源的配置值。此时对于ConfigurationFactory来说,两个关键的成员变量,我们已经初始化完毕了。
+
+```java
+public static volatile Configuration CURRENT_FILE_INSTANCE;
+
+public static volatile FileConfiguration ORIGIN_FILE_INSTANCE_REGISTRY;
+```
+
+最终:
+
+`CURRENT_FILE_INSTANCE` 成为后续统一的配置入口。
+
+#### 1.2.3 `maybeNeedOriginFileInstance()`
+该方法根据配置类型动态初始化 `file.conf` 配置
+
+```java
+private static void maybeNeedOriginFileInstance() {
+    if (ConfigType.File.name().equalsIgnoreCase(getConfigType())) {
+        String pathDataId = String.join(
+            ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
+            ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY
+        );
+        String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
+        ORIGIN_FILE_INSTANCE = new FileConfiguration(name);
+    }
+}
+```
+
+这里的 `getConfigType()` 使用了 `CURRENT_FILE_INSTANCE`,读取 `config.type` 判断配置来源(如 
`file`、`nacos`、`apollo` 等)。
+
+总结:
+
+这是一个按需初始化逻辑,只有当配置类型是 `file` 时,才会加载 `file.conf` 
作为额外配置,在这一步中其实是懒加载或按需,然后再决定是否初始化file.conf的逻辑。(file.conf文件多用于指定file模式为配置中心时的一些基本配置属性,这一点在seata内部的集成测试中大量使用)
+
+
+
+我们简单来看下getConfigType的逻辑,此时已经使用的CURRENT_FILE_INSTANCE对象来处理的了,那么对于config.type的获取就会走上一步中代理的逻辑。config.type是需要我们自己在本地配置文件进行指定的,用于决定对接的配置中心的类型。
+
+```java
+private static String getConfigType() {
+        String configTypeName = 
CURRENT_FILE_INSTANCE.getConfig(ConfigurationKeys.FILE_ROOT_CONFIG
+                + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
+                + ConfigurationKeys.FILE_ROOT_TYPE);
+        if (StringUtils.isBlank(configTypeName)) {
+            throw new NotSupportYetException("config type can not be null");
+        }
+        return configTypeName;
+    }
+```
+
+## 二、最后
+通过本文的分析,我们梳理了 **Seata 配置加载的第一个阶段——本地配置加载流程**:
+
++ 以 `SeataPropertiesLoader` 为入口,在 Spring Bean 初始化前介入,动态注入配置;
++ `ConfigurationFactory` 作为核心工厂,静态初始化阶段完成了三步关键逻辑:
+  1. 加载本地配置文件,构建 `ORIGIN_FILE_INSTANCE_REGISTRY`;
+  2. 通过 SPI 增强配置获取能力,生成统一入口 `CURRENT_FILE_INSTANCE`;
+  3. 根据 `config.type` 判断是否按需加载 `file.conf` 等附加配置。
++ 这一阶段确保 Seata 启动初期就能拿到完整的本地配置,为后续组件初始化奠定基础。
+
+不过,Seata 的配置体系并不止于此。实际生产环境中,大多数用户会结合配置中心(如 Nacos、Apollo、ZooKeeper 
等),实现配置的集中管理与动态刷新。而 **配置中心的加载与动态更新机制** 是 Seata 配置体系的第二个重要阶段。
+
+由于篇幅原因,本文只聚焦“本地配置加载”这部分核心逻辑。关于第二阶段的详细实现,我们会在下一篇文章中深入剖析,包括:
+
++ Seata 如何基于本地基础配置对接远程配置中心;
++ 动态刷新机制的原理和线程模型;
++ 如何优雅实现配置变更的实时生效。
+
+敬请期待下一篇:**「Seata 配置中心加载与动态刷新机制详解」**。
+
+
+
+
+


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to