From 16fae83f703cd71648493bf5626a6ae4c85a5fc4 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:03:48 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat(architectures):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E5=9F=BA=E7=B1=BB=E5=92=8C=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E7=BC=96=E6=8E=92=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 Architecture 基类,提供系统、模型、工具等组件的注册与管理功能 - 添加 ArchitectureBootstrapper 协调器,负责初始化期间的基础设施准备工作 - 实现生命周期管理、组件注册管理和模块管理功能 - 提供同步和异步初始化方法,支持不同场景下的架构初始化需求 - 添加架构上下文绑定和 IOC 容器冻结功能,确保运行时依赖图稳定 - 实现架构生命周期钩子注册和阶段变更事件功能 - 添加完整的单元测试验证初始化编排流程的正确性 --- ...ArchitectureInitializationPipelineTests.cs | 188 ++++++++++++++++++ GFramework.Core/Architectures/Architecture.cs | 71 ++----- .../Architectures/ArchitectureBootstrapper.cs | 118 +++++++++++ 3 files changed, 326 insertions(+), 51 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs create mode 100644 GFramework.Core/Architectures/ArchitectureBootstrapper.cs diff --git a/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs new file mode 100644 index 00000000..717e1b18 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs @@ -0,0 +1,188 @@ +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Events; +using GFramework.Core.Architectures; +using GFramework.Core.Environment; +using GFramework.Core.Logging; +using Microsoft.Extensions.DependencyInjection; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 验证 初始化编排流程的单元测试。 +/// 这些测试覆盖环境初始化、服务准备、上下文绑定和自定义服务配置的时序, +/// 以确保核心协调器在拆分后仍保持既有行为。 +/// +[TestFixture] +public class ArchitectureInitializationPipelineTests +{ + /// + /// 为每个测试准备独立的日志工厂和游戏上下文状态。 + /// + [SetUp] + public void SetUp() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + GameContext.Clear(); + } + + /// + /// 清理测试期间注册的全局游戏上下文,避免跨测试污染。 + /// + [TearDown] + public void TearDown() + { + GameContext.Clear(); + } + + /// + /// 验证异步初始化会在执行用户初始化逻辑之前准备环境、服务和上下文。 + /// + [Test] + public async Task InitializeAsync_Should_Prepare_Runtime_Before_OnInitialize() + { + var environment = new TrackingEnvironment(); + var marker = new BootstrapMarker(); + var architecture = new InitializationPipelineTestArchitecture(environment, marker); + + await architecture.InitializeAsync(); + + AssertRuntimePrepared(architecture, environment, marker); + await architecture.DestroyAsync(); + } + + /// + /// 验证同步初始化路径复用同一套基础设施准备流程。 + /// + [Test] + public async Task Initialize_Should_Prepare_Runtime_Before_OnInitialize() + { + var environment = new TrackingEnvironment(); + var marker = new BootstrapMarker(); + var architecture = new InitializationPipelineTestArchitecture(environment, marker); + + architecture.Initialize(); + + AssertRuntimePrepared(architecture, environment, marker); + await architecture.DestroyAsync(); + } + + /// + /// 断言初始化阶段所需的运行时准备工作都已经完成。 + /// + /// 待验证的测试架构实例。 + /// 测试使用的环境对象。 + /// 通过服务配置委托注册的标记服务。 + private static void AssertRuntimePrepared( + InitializationPipelineTestArchitecture architecture, + TrackingEnvironment environment, + BootstrapMarker marker) + { + Assert.Multiple(() => + { + Assert.That(architecture.ObservedEnvironmentInitialized, Is.True); + Assert.That(architecture.ObservedConfiguredServiceAvailable, Is.True); + Assert.That(architecture.ObservedEventBusAvailable, Is.True); + Assert.That(architecture.ObservedContextWasBound, Is.True); + Assert.That(architecture.ObservedEnvironmentRegistered, Is.True); + Assert.That(architecture.Context.GetEnvironment(), Is.SameAs(environment)); + Assert.That(architecture.Context.GetService(), Is.SameAs(marker)); + Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.Ready)); + }); + } + + /// + /// 跟踪初始化期间关键可观察状态的测试架构。 + /// + private sealed class InitializationPipelineTestArchitecture : Architecture + { + private readonly TrackingEnvironment _environment; + private readonly BootstrapMarker _marker; + + /// + /// 使用可观察环境和标记服务创建测试架构。 + /// + /// 用于验证初始化时序的环境对象。 + /// 用于验证服务钩子执行结果的标记服务。 + public InitializationPipelineTestArchitecture( + TrackingEnvironment environment, + BootstrapMarker marker) + : base(environment: environment) + { + _environment = environment; + _marker = marker; + } + + /// + /// 记录用户初始化逻辑执行时环境是否已经准备完成。 + /// + public bool ObservedEnvironmentInitialized { get; private set; } + + /// + /// 记录自定义服务是否已在用户初始化前注册到容器。 + /// + public bool ObservedConfiguredServiceAvailable { get; private set; } + + /// + /// 记录内置事件总线是否已在用户初始化前可用。 + /// + public bool ObservedEventBusAvailable { get; private set; } + + /// + /// 记录当前上下文是否已在用户初始化前绑定到全局游戏上下文表。 + /// + public bool ObservedContextWasBound { get; private set; } + + /// + /// 记录环境对象是否已在用户初始化前注册到架构上下文。 + /// + public bool ObservedEnvironmentRegistered { get; private set; } + + /// + /// 为容器注册测试标记服务,用于验证初始化前的服务钩子是否执行。 + /// + public override Action? Configurator => services => services.AddSingleton(_marker); + + /// + /// 在用户初始化逻辑中采集运行时准备状态。 + /// + protected override void OnInitialize() + { + ObservedEnvironmentInitialized = _environment.InitializeCallCount == 1; + ObservedConfiguredServiceAvailable = ReferenceEquals(Context.GetService(), _marker); + ObservedEventBusAvailable = Context.GetService() is not null; + ObservedContextWasBound = ReferenceEquals(GameContext.GetByType(GetType()), Context); + ObservedEnvironmentRegistered = ReferenceEquals(Context.GetEnvironment(), _environment); + } + } + + /// + /// 用于验证环境初始化是否发生的测试环境。 + /// + private sealed class TrackingEnvironment : EnvironmentBase + { + /// + /// 获取测试环境名称。 + /// + public override string Name { get; } = "Tracking"; + + /// + /// 获取环境初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + /// + /// 记录环境初始化次数。 + /// + public override void Initialize() + { + InitializeCallCount++; + } + } + + /// + /// 通过服务配置委托注册到容器的测试标记对象。 + /// + private sealed class BootstrapMarker + { + } +} \ No newline at end of file diff --git a/GFramework.Core/Architectures/Architecture.cs b/GFramework.Core/Architectures/Architecture.cs index c3e47436..cabffb0e 100644 --- a/GFramework.Core/Architectures/Architecture.cs +++ b/GFramework.Core/Architectures/Architecture.cs @@ -37,19 +37,25 @@ public abstract class Architecture : IArchitecture IArchitectureServices? services = null, IArchitectureContext? context = null) { - Configuration = configuration ?? new ArchitectureConfiguration(); - Environment = environment ?? new DefaultEnvironment(); - Services = services ?? new ArchitectureServices(); + var resolvedConfiguration = configuration ?? new ArchitectureConfiguration(); + var resolvedEnvironment = environment ?? new DefaultEnvironment(); + var resolvedServices = services ?? new ArchitectureServices(); _context = context; // 初始化 Logger - LoggerFactoryResolver.Provider = Configuration.LoggerProperties.LoggerFactoryProvider; + LoggerFactoryResolver.Provider = resolvedConfiguration.LoggerProperties.LoggerFactoryProvider; _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); // 初始化管理器 - _lifecycle = new ArchitectureLifecycle(this, Configuration, Services, _logger); - _componentRegistry = new ArchitectureComponentRegistry(this, Configuration, Services, _lifecycle, _logger); - _modules = new ArchitectureModules(this, Services, _logger); + _bootstrapper = new ArchitectureBootstrapper(GetType(), resolvedEnvironment, resolvedServices, _logger); + _lifecycle = new ArchitectureLifecycle(this, resolvedConfiguration, resolvedServices, _logger); + _componentRegistry = new ArchitectureComponentRegistry( + this, + resolvedConfiguration, + resolvedServices, + _lifecycle, + _logger); + _modules = new ArchitectureModules(this, resolvedServices, _logger); } #endregion @@ -70,21 +76,6 @@ public abstract class Architecture : IArchitecture #region Properties - /// - /// 获取架构配置对象 - /// - private IArchitectureConfiguration Configuration { get; } - - /// - /// 获取环境配置对象 - /// - private IEnvironment Environment { get; } - - /// - /// 获取服务管理器 - /// - private IArchitectureServices Services { get; } - /// /// 当前架构的阶段 /// @@ -129,6 +120,11 @@ public abstract class Architecture : IArchitecture /// private IArchitectureContext? _context; + /// + /// 初始化基础设施编排器 + /// + private readonly ArchitectureBootstrapper _bootstrapper; + /// /// 生命周期管理器 /// @@ -284,32 +280,7 @@ public abstract class Architecture : IArchitecture /// 是否启用异步模式 private async Task InitializeInternalAsync(bool asyncMode) { - // === 基础环境初始化 === - Environment.Initialize(); - - // 注册内置服务模块 - Services.ModuleManager.RegisterBuiltInModules(Services.Container); - - // 将 Environment 注册到容器 - if (!Services.Container.Contains()) - Services.Container.RegisterPlurality(Environment); - - // 初始化架构上下文 - _context ??= new ArchitectureContext(Services.Container); - GameContext.Bind(GetType(), _context); - - // 为服务设置上下文 - Services.SetContext(_context); - if (Configurator is null) - { - _logger.Debug("Mediator-based cqrs will not take effect without the service setter configured!"); - } - - // 执行服务钩子 - Services.Container.ExecuteServicesHook(Configurator); - - // 初始化服务模块 - await Services.ModuleManager.InitializeAllAsync(asyncMode); + _context = await _bootstrapper.PrepareForInitializationAsync(_context, Configurator, asyncMode); // === 用户 OnInitialize === _logger.Debug("Calling user OnInitialize()"); @@ -320,9 +291,7 @@ public abstract class Architecture : IArchitecture await _lifecycle.InitializeAllComponentsAsync(asyncMode); // === 初始化完成阶段 === - Services.Container.Freeze(); - _logger.Info("IOC container frozen"); - + _bootstrapper.CompleteInitialization(); _lifecycle.MarkAsReady(); _logger.Info($"Architecture {GetType().Name} is ready - all components initialized"); } diff --git a/GFramework.Core/Architectures/ArchitectureBootstrapper.cs b/GFramework.Core/Architectures/ArchitectureBootstrapper.cs new file mode 100644 index 00000000..984c0a07 --- /dev/null +++ b/GFramework.Core/Architectures/ArchitectureBootstrapper.cs @@ -0,0 +1,118 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Environment; +using GFramework.Core.Abstractions.Logging; +using Microsoft.Extensions.DependencyInjection; + +namespace GFramework.Core.Architectures; + +/// +/// 协调架构初始化期间的基础设施准备工作。 +/// 该类型将环境初始化、服务模块启动、上下文绑定和服务容器配置从 中拆出, +/// 使核心架构类只保留生命周期入口和公共 API 协调职责。 +/// +internal sealed class ArchitectureBootstrapper( + Type architectureType, + IEnvironment environment, + IArchitectureServices services, + ILogger logger) +{ + /// + /// 在执行用户 OnInitialize 之前准备架构运行时。 + /// 该流程必须保证环境、内置服务、上下文和服务钩子已经可用, + /// 因为用户初始化逻辑通常会立即访问事件总线、查询执行器或环境对象。 + /// + /// 调用方已经提供的上下文;如果为空则创建默认上下文。 + /// 可选的容器配置委托,用于接入 Mediator 等扩展服务。 + /// 是否以异步模式初始化服务模块。 + /// 已绑定到当前架构类型的架构上下文。 + public async Task PrepareForInitializationAsync( + IArchitectureContext? existingContext, + Action? configurator, + bool asyncMode) + { + InitializeEnvironment(); + RegisterBuiltInModules(); + EnsureEnvironmentRegistered(); + + var context = EnsureContext(existingContext); + ConfigureServices(context, configurator); + await InitializeServiceModulesAsync(asyncMode); + return context; + } + + /// + /// 完成用户组件初始化之后的收尾工作。 + /// 冻结容器可以阻止 Ready 阶段之后的意外服务注册,保持运行时依赖图稳定。 + /// + public void CompleteInitialization() + { + services.Container.Freeze(); + logger.Info("IOC container frozen"); + } + + /// + /// 初始化运行环境,使环境对象在后续服务构建和用户初始化前进入可用状态。 + /// + private void InitializeEnvironment() + { + environment.Initialize(); + } + + /// + /// 注册框架内置服务模块。 + /// 该步骤必须先于执行服务钩子,以便容器具备 CQRS 和事件总线等基础服务。 + /// + private void RegisterBuiltInModules() + { + services.ModuleManager.RegisterBuiltInModules(services.Container); + } + + /// + /// 确保环境对象可以通过架构容器解析。 + /// 如果调用方已经预先注册了自定义环境实例,则保留现有绑定,避免覆盖外部配置。 + /// + private void EnsureEnvironmentRegistered() + { + if (!services.Container.Contains()) + services.Container.RegisterPlurality(environment); + } + + /// + /// 获取本次初始化使用的架构上下文,并将其绑定到全局游戏上下文表。 + /// 绑定发生在用户初始化之前,确保组件在注册阶段即可通过架构类型解析上下文。 + /// + /// 外部提供的上下文。 + /// 实际用于本次初始化的上下文实例。 + private IArchitectureContext EnsureContext(IArchitectureContext? existingContext) + { + var context = existingContext ?? new ArchitectureContext(services.Container); + GameContext.Bind(architectureType, context); + return context; + } + + /// + /// 为服务容器设置上下文并执行扩展配置钩子。 + /// 这一步统一承接 Mediator 等容器扩展的接入点,避免 直接操作容器细节。 + /// + /// 当前架构上下文。 + /// 可选的服务集合配置委托。 + private void ConfigureServices(IArchitectureContext context, Action? configurator) + { + services.SetContext(context); + + if (configurator is null) + logger.Debug("Mediator-based cqrs will not take effect without the service setter configured!"); + + services.Container.ExecuteServicesHook(configurator); + } + + /// + /// 初始化所有服务模块。 + /// 该过程在用户注册系统、模型和工具之前完成,避免组件在初始化期间访问未准备好的服务。 + /// + /// 是否允许异步初始化服务模块。 + private async Task InitializeServiceModulesAsync(bool asyncMode) + { + await services.ModuleManager.InitializeAllAsync(asyncMode); + } +} \ No newline at end of file From 01dc1523a52068948a85ab20405cec740ba24043 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:52:21 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat(architecture):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E6=A0=B8=E5=BF=83=E7=BB=84=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5=E5=AE=B9=E5=99=A8=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 Architecture 基类,提供系统、模型、工具等组件的注册与管理功能 - 添加 ArchitectureComponentRegistry 组件注册管理器,负责管理系统、模型、工具的注册 - 实现 ArchitectureLifecycle 生命周期管理器,管理架构阶段转换和组件初始化 - 添加 ArchitectureModules 模块管理器,负责架构模块安装和中介行为注册 - 实现 MicrosoftDiContainer 依赖注入容器,包装 Microsoft.Extensions.DependencyInjection - 提供完整的组件生命周期管理,支持同步和异步初始化模式 - 添加架构阶段管理和组件注册验证机制 --- .../Architectures/IArchitecture.cs | 4 +- ...hitectureComponentRegistryBehaviorTests.cs | 306 ++++++++++++++++++ .../ArchitectureLifecycleBehaviorTests.cs | 253 +++++++++++++++ .../ArchitectureModulesBehaviorTests.cs | 188 +++++++++++ GFramework.Core/Architectures/Architecture.cs | 4 +- .../ArchitectureComponentRegistry.cs | 2 + .../Architectures/ArchitectureDisposer.cs | 111 +++++++ .../Architectures/ArchitectureLifecycle.cs | 211 ++---------- .../Architectures/ArchitectureModules.cs | 3 +- .../ArchitecturePhaseCoordinator.cs | 116 +++++++ GFramework.Core/Ioc/MicrosoftDiContainer.cs | 37 ++- 11 files changed, 1046 insertions(+), 189 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs create mode 100644 GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs create mode 100644 GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs create mode 100644 GFramework.Core/Architectures/ArchitectureDisposer.cs create mode 100644 GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs diff --git a/GFramework.Core.Abstractions/Architectures/IArchitecture.cs b/GFramework.Core.Abstractions/Architectures/IArchitecture.cs index c2ba8fd6..0055d3fe 100644 --- a/GFramework.Core.Abstractions/Architectures/IArchitecture.cs +++ b/GFramework.Core.Abstractions/Architectures/IArchitecture.cs @@ -74,7 +74,9 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia /// /// 注册中介行为管道 - /// 用于配置Mediator框架的行为拦截和处理逻辑 + /// 用于配置Mediator框架的行为拦截和处理逻辑。 + /// 既支持实现 IPipelineBehavior<,> 的开放泛型行为类型, + /// 也支持绑定到单一请求/响应对的封闭行为类型。 /// /// 行为类型,必须是引用类型 void RegisterMediatorBehavior() diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs new file mode 100644 index 00000000..74344191 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs @@ -0,0 +1,306 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Architectures; +using GFramework.Core.Logging; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 验证 Architecture 通过 ArchitectureComponentRegistry 暴露出的组件注册行为。 +/// 这些测试覆盖实例注册、工厂注册、上下文注入、生命周期初始化和 Ready 后注册约束, +/// 用于保护组件注册器在继续重构后的既有契约。 +/// +[TestFixture] +public class ArchitectureComponentRegistryBehaviorTests +{ + /// + /// 初始化日志工厂和全局上下文状态。 + /// + [SetUp] + public void SetUp() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + GameContext.Clear(); + } + + /// + /// 清理测试过程中绑定到全局表的架构上下文。 + /// + [TearDown] + public void TearDown() + { + GameContext.Clear(); + } + + /// + /// 验证系统实例注册会注入上下文并参与生命周期初始化。 + /// + [Test] + public async Task RegisterSystem_Instance_Should_Set_Context_And_Initialize_System() + { + var system = new TrackingSystem(); + var architecture = new RegistryTestArchitecture(target => target.RegisterSystem(system)); + + await architecture.InitializeAsync(); + + Assert.Multiple(() => + { + Assert.That(system.GetContext(), Is.SameAs(architecture.Context)); + Assert.That(system.InitializeCallCount, Is.EqualTo(1)); + Assert.That(architecture.Context.GetSystem(), Is.SameAs(system)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证模型实例注册会注入上下文并参与生命周期初始化。 + /// + [Test] + public async Task RegisterModel_Instance_Should_Set_Context_And_Initialize_Model() + { + var model = new TrackingModel(); + var architecture = new RegistryTestArchitecture(target => target.RegisterModel(model)); + + await architecture.InitializeAsync(); + + Assert.Multiple(() => + { + Assert.That(model.GetContext(), Is.SameAs(architecture.Context)); + Assert.That(model.InitializeCallCount, Is.EqualTo(1)); + Assert.That(architecture.Context.GetModel(), Is.SameAs(model)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证上下文工具注册会注入上下文并参与生命周期初始化。 + /// + [Test] + public async Task RegisterUtility_Instance_Should_Set_Context_For_ContextUtility() + { + var utility = new TrackingContextUtility(); + var architecture = new RegistryTestArchitecture(target => target.RegisterUtility(utility)); + + await architecture.InitializeAsync(); + + Assert.Multiple(() => + { + Assert.That(utility.GetContext(), Is.SameAs(architecture.Context)); + Assert.That(utility.InitializeCallCount, Is.EqualTo(1)); + Assert.That(architecture.Context.GetUtility(), Is.SameAs(utility)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证普通工具的工厂注册会在首次解析时创建单例并执行创建回调。 + /// + [Test] + public async Task RegisterUtility_Type_Should_Create_Singleton_And_Invoke_Callback() + { + FactoryCreatedUtility? callbackInstance = null; + var architecture = new RegistryTestArchitecture(target => + target.RegisterUtility(created => callbackInstance = created)); + + await architecture.InitializeAsync(); + + var first = architecture.Context.GetUtility(); + var second = architecture.Context.GetUtility(); + + Assert.Multiple(() => + { + Assert.That(callbackInstance, Is.SameAs(first)); + Assert.That(second, Is.SameAs(first)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证 Ready 阶段后不允许继续注册 Utility,保持与系统和模型一致的约束。 + /// + [Test] + public async Task RegisterUtility_After_Ready_Should_Throw_InvalidOperationException() + { + var architecture = new RegistryTestArchitecture(_ => { }); + await architecture.InitializeAsync(); + + Assert.That( + () => architecture.RegisterUtility(new TrackingContextUtility()), + Throws.InvalidOperationException.With.Message.EqualTo( + "Cannot register utility after Architecture is Ready")); + + await architecture.DestroyAsync(); + } + + /// + /// 用于测试组件注册行为的最小架构实现。 + /// + private sealed class RegistryTestArchitecture(Action registrationAction) : Architecture + { + /// + /// 在初始化阶段执行测试注入的注册逻辑。 + /// + protected override void OnInitialize() + { + registrationAction(this); + } + } + + /// + /// 记录初始化与上下文注入情况的测试系统。 + /// + private sealed class TrackingSystem : ISystem + { + private IArchitectureContext _context = null!; + + /// + /// 获取系统初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + /// + /// 记录初始化调用。 + /// + public void Initialize() + { + InitializeCallCount++; + } + + /// + /// 该测试系统不关心阶段变更。 + /// + /// 当前架构阶段。 + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + /// + /// 存储注入的架构上下文。 + /// + /// 架构上下文。 + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + /// + /// 返回当前持有的架构上下文。 + /// + public IArchitectureContext GetContext() + { + return _context; + } + + /// + /// 该测试系统没有额外销毁逻辑。 + /// + public void Destroy() + { + } + } + + /// + /// 记录初始化与上下文注入情况的测试模型。 + /// + private sealed class TrackingModel : IModel + { + private IArchitectureContext _context = null!; + + /// + /// 获取模型初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + /// + /// 记录初始化调用。 + /// + public void Initialize() + { + InitializeCallCount++; + } + + /// + /// 该测试模型不关心阶段变更。 + /// + /// 当前架构阶段。 + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + /// + /// 存储注入的架构上下文。 + /// + /// 架构上下文。 + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + /// + /// 返回当前持有的架构上下文。 + /// + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 记录初始化与上下文注入情况的测试上下文工具。 + /// + private sealed class TrackingContextUtility : IContextUtility + { + private IArchitectureContext _context = null!; + + /// + /// 获取工具初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + /// + /// 记录初始化调用。 + /// + public void Initialize() + { + InitializeCallCount++; + } + + /// + /// 存储注入的架构上下文。 + /// + /// 架构上下文。 + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + /// + /// 返回当前持有的架构上下文。 + /// + public IArchitectureContext GetContext() + { + return _context; + } + + /// + /// 该测试工具没有额外销毁逻辑。 + /// + public void Destroy() + { + } + } + + /// + /// 用于验证普通工厂注册路径的简单工具。 + /// + private sealed class FactoryCreatedUtility : IUtility + { + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs new file mode 100644 index 00000000..0bde4b5d --- /dev/null +++ b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs @@ -0,0 +1,253 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Lifecycle; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Architectures; +using GFramework.Core.Logging; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 验证 Architecture 生命周期行为的集成测试。 +/// 这些测试覆盖阶段流转、失败状态传播和逆序销毁规则, +/// 用于保护拆分后的生命周期管理、阶段协调与销毁协调行为。 +/// +[TestFixture] +public class ArchitectureLifecycleBehaviorTests +{ + /// + /// 为每个测试准备独立的日志工厂和全局上下文状态。 + /// + [SetUp] + public void SetUp() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + GameContext.Clear(); + } + + /// + /// 清理测试注册到全局上下文表的架构上下文。 + /// + [TearDown] + public void TearDown() + { + GameContext.Clear(); + } + + /// + /// 验证初始化流程会按既定顺序推进所有生命周期阶段。 + /// + [Test] + public async Task InitializeAsync_Should_Enter_Expected_Phases_In_Order() + { + var architecture = new PhaseTrackingArchitecture(); + + await architecture.InitializeAsync(); + + Assert.That(architecture.PhaseHistory, Is.EqualTo(new[] + { + ArchitecturePhase.BeforeUtilityInit, + ArchitecturePhase.AfterUtilityInit, + ArchitecturePhase.BeforeModelInit, + ArchitecturePhase.AfterModelInit, + ArchitecturePhase.BeforeSystemInit, + ArchitecturePhase.AfterSystemInit, + ArchitecturePhase.Ready + })); + + await architecture.DestroyAsync(); + } + + /// + /// 验证用户初始化失败时,等待 Ready 的任务会失败并进入 FailedInitialization 阶段。 + /// + [Test] + public async Task InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization() + { + var architecture = new PhaseTrackingArchitecture(() => throw new InvalidOperationException("boom")); + + var exception = Assert.ThrowsAsync(async () => await architecture.InitializeAsync()); + Assert.That(exception, Is.Not.Null); + Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.FailedInitialization)); + Assert.ThrowsAsync(async () => await architecture.WaitUntilReadyAsync()); + } + + /// + /// 验证销毁流程会按注册逆序释放组件,并推进 Destroying/Destroyed 阶段。 + /// + [Test] + public async Task DestroyAsync_Should_Destroy_Components_In_Reverse_Registration_Order() + { + var destroyOrder = new List(); + var architecture = new DestroyOrderArchitecture(destroyOrder); + + await architecture.InitializeAsync(); + await architecture.DestroyAsync(); + + Assert.Multiple(() => + { + Assert.That(destroyOrder, Is.EqualTo(new[] { "system", "model", "utility" })); + Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.Destroyed)); + Assert.That(architecture.PhaseHistory[^2..], Is.EqualTo(new[] + { + ArchitecturePhase.Destroying, + ArchitecturePhase.Destroyed + })); + }); + } + + /// + /// 记录阶段流转的可配置测试架构。 + /// + private sealed class PhaseTrackingArchitecture : Architecture + { + private readonly Action? _onInitializeAction; + + /// + /// 创建一个可选地在用户初始化阶段执行自定义逻辑的测试架构。 + /// + /// 用户初始化时执行的测试回调。 + public PhaseTrackingArchitecture(Action? onInitializeAction = null) + { + _onInitializeAction = onInitializeAction; + PhaseChanged += phase => PhaseHistory.Add(phase); + } + + /// + /// 获取架构经历过的阶段列表。 + /// + public List PhaseHistory { get; } = []; + + /// + /// 执行测试注入的初始化逻辑。 + /// + protected override void OnInitialize() + { + _onInitializeAction?.Invoke(); + } + } + + /// + /// 在初始化时注册可销毁组件的测试架构。 + /// + private sealed class DestroyOrderArchitecture : Architecture + { + private readonly List _destroyOrder; + + /// + /// 创建用于验证销毁顺序的测试架构。 + /// + /// 记录组件销毁顺序的列表。 + public DestroyOrderArchitecture(List destroyOrder) + { + _destroyOrder = destroyOrder; + PhaseChanged += phase => PhaseHistory.Add(phase); + } + + /// + /// 获取架构经历过的阶段列表。 + /// + public List PhaseHistory { get; } = []; + + /// + /// 注册会记录销毁顺序的 Utility、Model 和 System。 + /// + protected override void OnInitialize() + { + RegisterUtility(new TrackingDestroyableUtility(_destroyOrder)); + RegisterModel(new TrackingDestroyableModel(_destroyOrder)); + RegisterSystem(new TrackingDestroyableSystem(_destroyOrder)); + } + } + + /// + /// 用于验证逆序销毁的上下文工具。 + /// + private sealed class TrackingDestroyableUtility(List destroyOrder) : IContextUtility + { + private IArchitectureContext _context = null!; + + public void Initialize() + { + } + + public void Destroy() + { + destroyOrder.Add("utility"); + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于验证逆序销毁的模型。 + /// + private sealed class TrackingDestroyableModel(List destroyOrder) : IModel, IDestroyable + { + private IArchitectureContext _context = null!; + + public void Destroy() + { + destroyOrder.Add("model"); + } + + public void Initialize() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于验证逆序销毁的系统。 + /// + private sealed class TrackingDestroyableSystem(List destroyOrder) : ISystem + { + private IArchitectureContext _context = null!; + + public void Initialize() + { + } + + public void Destroy() + { + destroyOrder.Add("system"); + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs new file mode 100644 index 00000000..05601230 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs @@ -0,0 +1,188 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Architectures; +using GFramework.Core.Logging; +using Mediator; +using Microsoft.Extensions.DependencyInjection; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 验证 Architecture 通过 ArchitectureModules 暴露出的模块安装与 Mediator 行为注册能力。 +/// 这些测试覆盖模块安装回调和中介管道行为接入,确保模块管理器仍然保持可观察行为不变。 +/// +[TestFixture] +public class ArchitectureModulesBehaviorTests +{ + /// + /// 初始化日志工厂和全局上下文状态。 + /// + [SetUp] + public void SetUp() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + GameContext.Clear(); + TrackingPipelineBehavior.InvocationCount = 0; + } + + /// + /// 清理测试过程中写入的全局上下文状态。 + /// + [TearDown] + public void TearDown() + { + GameContext.Clear(); + TrackingPipelineBehavior.InvocationCount = 0; + } + + /// + /// 验证安装模块时会把当前架构实例传给模块,并允许模块在安装阶段注册组件。 + /// + [Test] + public async Task InstallModule_Should_Invoke_Module_Install_With_Current_Architecture() + { + var module = new TrackingArchitectureModule(); + var architecture = new ModuleTestArchitecture(target => target.InstallModule(module)); + + await architecture.InitializeAsync(); + + Assert.Multiple(() => + { + Assert.That(module.InstalledArchitecture, Is.SameAs(architecture)); + Assert.That(module.InstallCallCount, Is.EqualTo(1)); + Assert.That(architecture.Context.GetUtility(), Is.Not.Null); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证注册的 Mediator 行为会参与请求管道执行。 + /// + [Test] + public async Task RegisterMediatorBehavior_Should_Apply_Pipeline_Behavior_To_Request() + { + var architecture = new ModuleTestArchitecture(target => + target.RegisterMediatorBehavior>()); + + await architecture.InitializeAsync(); + + var response = await architecture.Context.SendRequestAsync(new ModuleBehaviorRequest()); + + Assert.Multiple(() => + { + Assert.That(response, Is.EqualTo("handled")); + Assert.That(TrackingPipelineBehavior.InvocationCount, Is.EqualTo(1)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 用于测试模块行为的最小架构实现。 + /// + private sealed class ModuleTestArchitecture(Action registrationAction) : Architecture + { + /// + /// 打开 Mediator 服务注册,以便测试中介行为接入。 + /// + public override Action? Configurator => + services => services.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; }); + + /// + /// 在初始化阶段执行测试注入的模块注册逻辑。 + /// + protected override void OnInitialize() + { + registrationAction(this); + } + } + + /// + /// 记录模块安装调用情况的测试模块。 + /// + private sealed class TrackingArchitectureModule : IArchitectureModule + { + /// + /// 获取模块安装调用次数。 + /// + public int InstallCallCount { get; private set; } + + /// + /// 获取最近一次接收到的架构实例。 + /// + public IArchitecture? InstalledArchitecture { get; private set; } + + /// + /// 记录安装调用,并在安装阶段注册一个工具验证调用链可用。 + /// + /// 目标架构实例。 + public void Install(IArchitecture architecture) + { + InstallCallCount++; + InstalledArchitecture = architecture; + architecture.RegisterUtility(new InstalledByModuleUtility()); + } + } + + /// + /// 由测试模块安装时注册的简单工具。 + /// + private sealed class InstalledByModuleUtility : IUtility + { + } +} + +/// +/// 用于验证管道行为注册是否生效的测试请求。 +/// +public sealed class ModuleBehaviorRequest : IRequest +{ +} + +/// +/// 处理测试请求的处理器。 +/// +public sealed class ModuleBehaviorRequestHandler : IRequestHandler +{ + /// + /// 返回固定结果,便于聚焦验证管道行为是否执行。 + /// + /// 请求实例。 + /// 取消令牌。 + /// 固定响应内容。 + public ValueTask Handle(ModuleBehaviorRequest request, CancellationToken cancellationToken) + { + return ValueTask.FromResult("handled"); + } +} + +/// +/// 记录请求通过管道次数的测试行为。 +/// +/// 请求类型。 +/// 响应类型。 +public sealed class TrackingPipelineBehavior : IPipelineBehavior + where TRequest : IRequest +{ + /// + /// 获取当前测试进程中该请求类型对应的行为触发次数。 + /// + public static int InvocationCount { get; set; } + + /// + /// 记录一次行为执行,然后继续执行下一个处理器。 + /// + /// 当前请求消息。 + /// 下一个处理委托。 + /// 取消令牌。 + /// 下游处理器的响应结果。 + public async ValueTask Handle( + TRequest message, + MessageHandlerDelegate next, + CancellationToken cancellationToken) + { + InvocationCount++; + return await next(message, cancellationToken); + } +} \ No newline at end of file diff --git a/GFramework.Core/Architectures/Architecture.cs b/GFramework.Core/Architectures/Architecture.cs index cabffb0e..af96d03f 100644 --- a/GFramework.Core/Architectures/Architecture.cs +++ b/GFramework.Core/Architectures/Architecture.cs @@ -16,6 +16,7 @@ namespace GFramework.Core.Architectures; /// 专注于生命周期管理、初始化流程控制和架构阶段转换。 /// /// 重构说明:此类已重构为协调器模式,将职责委托给专门的管理器: +/// - ArchitectureBootstrapper: 初始化基础设施编排 /// - ArchitectureLifecycle: 生命周期管理 /// - ArchitectureComponentRegistry: 组件注册管理 /// - ArchitectureModules: 模块管理 @@ -146,7 +147,8 @@ public abstract class Architecture : IArchitecture /// /// 注册中介行为管道 - /// 用于配置Mediator框架的行为拦截和处理逻辑 + /// 用于配置Mediator框架的行为拦截和处理逻辑。 + /// 可以传入开放泛型行为类型,也可以传入绑定到特定请求的封闭行为类型。 /// /// 行为类型,必须是引用类型 public void RegisterMediatorBehavior() where TBehavior : class diff --git a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs index d39d5972..91b2a846 100644 --- a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs +++ b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs @@ -155,6 +155,7 @@ internal sealed class ArchitectureComponentRegistry( /// 注册成功的工具实例 public TUtility RegisterUtility(TUtility utility) where TUtility : IUtility { + ValidateRegistration("utility"); logger.Debug($"Registering utility: {typeof(TUtility).Name}"); // 处理上下文工具类型的设置和生命周期管理 @@ -177,6 +178,7 @@ internal sealed class ArchitectureComponentRegistry( /// 可选的实例创建后回调 public void RegisterUtility(Action? onCreated = null) where T : class, IUtility { + ValidateRegistration("utility"); logger.Debug($"Registering utility type: {typeof(T).Name}"); services.Container.RegisterFactory(sp => diff --git a/GFramework.Core/Architectures/ArchitectureDisposer.cs b/GFramework.Core/Architectures/ArchitectureDisposer.cs new file mode 100644 index 00000000..1c1a861d --- /dev/null +++ b/GFramework.Core/Architectures/ArchitectureDisposer.cs @@ -0,0 +1,111 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Lifecycle; +using GFramework.Core.Abstractions.Logging; + +namespace GFramework.Core.Architectures; + +/// +/// 统一处理架构内可销毁对象的登记与释放。 +/// 该类型封装逆序销毁、异常隔离和服务模块清理规则, +/// 让 可以专注于初始化流程本身。 +/// +internal sealed class ArchitectureDisposer( + IArchitectureServices services, + ILogger logger) +{ + /// + /// 保留注册顺序的可销毁对象列表。 + /// 销毁时按逆序遍历,以尽量匹配组件间的依赖方向。 + /// + private readonly List _disposables = []; + + /// + /// 用于去重的可销毁对象集合。 + /// + private readonly HashSet _disposableSet = []; + + /// + /// 注册一个需要参与架构销毁流程的对象。 + /// 只有实现 的对象会被跟踪。 + /// + /// 待检查的组件实例。 + public void Register(object component) + { + if (component is not (IDestroyable or IAsyncDestroyable)) + return; + + if (!_disposableSet.Add(component)) + return; + + _disposables.Add(component); + logger.Trace($"Registered {component.GetType().Name} for destruction"); + } + + /// + /// 执行架构销毁流程。 + /// 该方法会根据当前阶段决定是否进入 Destroying/Destroyed 阶段,并负责服务模块与容器清理。 + /// + /// 销毁开始前的架构阶段。 + /// 用于推进架构阶段的回调。 + public async ValueTask DestroyAsync(ArchitecturePhase currentPhase, Action enterPhase) + { + if (currentPhase >= ArchitecturePhase.Destroying) + { + logger.Warn("Architecture destroy called but already in destroying/destroyed state"); + return; + } + + if (currentPhase == ArchitecturePhase.None) + { + logger.Debug("Architecture destroy called but never initialized, cleaning up registered components"); + await CleanupComponentsAsync(); + return; + } + + logger.Info("Starting architecture destruction"); + enterPhase(ArchitecturePhase.Destroying); + + await CleanupComponentsAsync(); + await services.ModuleManager.DestroyAllAsync(); + services.Container.Clear(); + + enterPhase(ArchitecturePhase.Destroyed); + logger.Info("Architecture destruction completed"); + } + + /// + /// 逆序销毁当前已注册的所有可销毁组件。 + /// 单个组件失败不会中断后续清理,避免在销毁阶段留下半清理状态。 + /// + private async ValueTask CleanupComponentsAsync() + { + logger.Info($"Destroying {_disposables.Count} disposable components"); + + for (var i = _disposables.Count - 1; i >= 0; i--) + { + var component = _disposables[i]; + + try + { + logger.Debug($"Destroying component: {component.GetType().Name}"); + + if (component is IAsyncDestroyable asyncDestroyable) + { + await asyncDestroyable.DestroyAsync(); + } + else if (component is IDestroyable destroyable) + { + destroyable.Destroy(); + } + } + catch (Exception ex) + { + logger.Error($"Error destroying {component.GetType().Name}", ex); + } + } + + _disposables.Clear(); + _disposableSet.Clear(); + } +} \ No newline at end of file diff --git a/GFramework.Core/Architectures/ArchitectureLifecycle.cs b/GFramework.Core/Architectures/ArchitectureLifecycle.cs index dbe26291..85c06ed7 100644 --- a/GFramework.Core/Architectures/ArchitectureLifecycle.cs +++ b/GFramework.Core/Architectures/ArchitectureLifecycle.cs @@ -27,11 +27,7 @@ internal sealed class ArchitectureLifecycle( /// 注册的钩子实例 public IArchitectureLifecycleHook RegisterLifecycleHook(IArchitectureLifecycleHook hook) { - if (CurrentPhase >= ArchitecturePhase.Ready && !configuration.ArchitectureProperties.AllowLateRegistration) - throw new InvalidOperationException( - "Cannot register lifecycle hook after architecture is Ready"); - _lifecycleHooks.Add(hook); - return hook; + return _phaseCoordinator.RegisterLifecycleHook(hook); } #endregion @@ -44,31 +40,32 @@ internal sealed class ArchitectureLifecycle( /// 要注册的组件 public void RegisterLifecycleComponent(object component) { - // 处理初始化 if (component is IInitializable initializable) { - if (!_initialized) + if (_initialized) + throw new InvalidOperationException("Cannot initialize component after Architecture is Ready"); + + if (_pendingInitializableSet.Add(initializable)) { - // 原子去重:HashSet.Add 返回 true 表示添加成功(之前不存在) - if (_pendingInitializableSet.Add(initializable)) - { - _pendingInitializableList.Add(initializable); - logger.Trace($"Added {component.GetType().Name} to pending initialization queue"); - } - } - else - { - throw new InvalidOperationException( - "Cannot initialize component after Architecture is Ready"); + _pendingInitializableList.Add(initializable); + logger.Trace($"Added {component.GetType().Name} to pending initialization queue"); } } - // 处理销毁(支持 IDestroyable 或 IAsyncDestroyable) - if (component is not (IDestroyable or IAsyncDestroyable)) return; - // 原子去重:HashSet.Add 返回 true 表示添加成功(之前不存在) - if (!_disposableSet.Add(component)) return; - _disposables.Add(component); - logger.Trace($"Registered {component.GetType().Name} for destruction"); + _disposer.Register(component); + } + + #endregion + + #region Phase Management + + /// + /// 进入指定的架构阶段,并执行相应的生命周期管理操作 + /// + /// 要进入的下一个架构阶段 + public void EnterPhase(ArchitecturePhase next) + { + _phaseCoordinator.EnterPhase(next); } #endregion @@ -88,19 +85,15 @@ internal sealed class ArchitectureLifecycle( private readonly List _pendingInitializableList = []; /// - /// 可销毁组件的去重集合(支持 IDestroyable 和 IAsyncDestroyable) + /// 架构阶段协调器 /// - private readonly HashSet _disposableSet = []; + private readonly ArchitecturePhaseCoordinator _phaseCoordinator = + new(architecture, configuration, services, logger); /// - /// 存储所有需要销毁的组件(统一管理,保持注册逆序销毁) + /// 架构销毁协调器 /// - private readonly List _disposables = []; - - /// - /// 生命周期感知对象列表 - /// - private readonly List _lifecycleHooks = []; + private readonly ArchitectureDisposer _disposer = new(services, logger); /// /// 标记架构是否已初始化完成 @@ -114,7 +107,7 @@ internal sealed class ArchitectureLifecycle( /// /// 当前架构的阶段 /// - public ArchitecturePhase CurrentPhase { get; private set; } + public ArchitecturePhase CurrentPhase => _phaseCoordinator.CurrentPhase; /// /// 获取一个布尔值,指示当前架构是否处于就绪状态 @@ -129,87 +122,10 @@ internal sealed class ArchitectureLifecycle( /// /// 阶段变更事件(用于测试和扩展) /// - public event Action? PhaseChanged; - - #endregion - - #region Phase Management - - /// - /// 进入指定的架构阶段,并执行相应的生命周期管理操作 - /// - /// 要进入的下一个架构阶段 - /// 当阶段转换不被允许时抛出异常 - public void EnterPhase(ArchitecturePhase next) + public event Action? PhaseChanged { - // 验证阶段转换 - ValidatePhaseTransition(next); - - // 执行阶段转换 - var previousPhase = CurrentPhase; - CurrentPhase = next; - - if (previousPhase != next) - logger.Info($"Architecture phase changed: {previousPhase} -> {next}"); - - // 通知阶段变更 - NotifyPhase(next); - NotifyPhaseAwareObjects(next); - - // 触发阶段变更事件(用于测试和扩展) - PhaseChanged?.Invoke(next); - } - - /// - /// 验证阶段转换是否合法 - /// - /// 目标阶段 - /// 当阶段转换不合法时抛出 - private void ValidatePhaseTransition(ArchitecturePhase next) - { - // 不需要严格验证,直接返回 - if (!configuration.ArchitectureProperties.StrictPhaseValidation) - return; - - // FailedInitialization 可以从任何阶段转换,直接返回 - if (next == ArchitecturePhase.FailedInitialization) - return; - - // 检查转换是否在允许列表中 - if (ArchitectureConstants.PhaseTransitions.TryGetValue(CurrentPhase, out var allowed) && - allowed.Contains(next)) - return; - - // 转换不合法,抛出异常 - var errorMsg = $"Invalid phase transition: {CurrentPhase} -> {next}"; - logger.Fatal(errorMsg); - throw new InvalidOperationException(errorMsg); - } - - /// - /// 通知所有架构阶段感知对象阶段变更 - /// - /// 新阶段 - private void NotifyPhaseAwareObjects(ArchitecturePhase phase) - { - foreach (var obj in services.Container.GetAll()) - { - logger.Trace($"Notifying phase-aware object {obj.GetType().Name} of phase change to {phase}"); - obj.OnArchitecturePhase(phase); - } - } - - /// - /// 通知所有生命周期钩子当前阶段变更 - /// - /// 当前架构阶段 - private void NotifyPhase(ArchitecturePhase phase) - { - foreach (var hook in _lifecycleHooks) - { - hook.OnPhase(phase, architecture); - logger.Trace($"Notifying lifecycle hook {hook.GetType().Name} of phase {phase}"); - } + add => _phaseCoordinator.PhaseChanged += value; + remove => _phaseCoordinator.PhaseChanged -= value; } #endregion @@ -311,72 +227,7 @@ internal sealed class ArchitectureLifecycle( /// public async ValueTask DestroyAsync() { - // 检查当前阶段,如果已经处于销毁或已销毁状态则直接返回 - if (CurrentPhase >= ArchitecturePhase.Destroying) - { - logger.Warn("Architecture destroy called but already in destroying/destroyed state"); - return; - } - - // 如果从未初始化(None 阶段),只清理已注册的组件,不进行阶段转换 - if (CurrentPhase == ArchitecturePhase.None) - { - logger.Debug("Architecture destroy called but never initialized, cleaning up registered components"); - await CleanupComponentsAsync(); - return; - } - - // 进入销毁阶段 - logger.Info("Starting architecture destruction"); - EnterPhase(ArchitecturePhase.Destroying); - - // 清理所有组件 - await CleanupComponentsAsync(); - - // 销毁服务模块 - await services.ModuleManager.DestroyAllAsync(); - - services.Container.Clear(); - - // 进入已销毁阶段 - EnterPhase(ArchitecturePhase.Destroyed); - logger.Info("Architecture destruction completed"); - } - - /// - /// 清理所有已注册的可销毁组件 - /// - private async ValueTask CleanupComponentsAsync() - { - // 销毁所有实现了 IAsyncDestroyable 或 IDestroyable 的组件(按注册逆序销毁) - logger.Info($"Destroying {_disposables.Count} disposable components"); - - for (var i = _disposables.Count - 1; i >= 0; i--) - { - var component = _disposables[i]; - try - { - logger.Debug($"Destroying component: {component.GetType().Name}"); - - // 优先使用异步销毁 - if (component is IAsyncDestroyable asyncDestroyable) - { - await asyncDestroyable.DestroyAsync(); - } - else if (component is IDestroyable destroyable) - { - destroyable.Destroy(); - } - } - catch (Exception ex) - { - logger.Error($"Error destroying {component.GetType().Name}", ex); - // 继续销毁其他组件,不会因为一个组件失败而中断 - } - } - - _disposables.Clear(); - _disposableSet.Clear(); + await _disposer.DestroyAsync(CurrentPhase, EnterPhase); } /// diff --git a/GFramework.Core/Architectures/ArchitectureModules.cs b/GFramework.Core/Architectures/ArchitectureModules.cs index 3b60cc25..94acae78 100644 --- a/GFramework.Core/Architectures/ArchitectureModules.cs +++ b/GFramework.Core/Architectures/ArchitectureModules.cs @@ -14,7 +14,8 @@ internal sealed class ArchitectureModules( { /// /// 注册中介行为管道 - /// 用于配置Mediator框架的行为拦截和处理逻辑 + /// 用于配置Mediator框架的行为拦截和处理逻辑。 + /// 支持开放泛型行为类型和针对单一请求的封闭行为类型。 /// /// 行为类型,必须是引用类型 public void RegisterMediatorBehavior() where TBehavior : class diff --git a/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs b/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs new file mode 100644 index 00000000..e1fc5f9d --- /dev/null +++ b/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs @@ -0,0 +1,116 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Logging; + +namespace GFramework.Core.Architectures; + +/// +/// 负责架构阶段流转的验证与通知。 +/// 该类型集中管理阶段值、生命周期钩子和阶段监听器,避免 +/// 同时承担阶段广播与组件初始化队列管理两类职责。 +/// +internal sealed class ArchitecturePhaseCoordinator( + IArchitecture architecture, + IArchitectureConfiguration configuration, + IArchitectureServices services, + ILogger logger) +{ + private readonly List _lifecycleHooks = []; + + /// + /// 获取当前架构阶段。 + /// + public ArchitecturePhase CurrentPhase { get; private set; } + + /// + /// 在架构阶段变更时触发。 + /// 该事件用于测试和扩展场景,保持现有公共行为不变。 + /// + public event Action? PhaseChanged; + + /// + /// 注册一个生命周期钩子。 + /// 就绪后是否允许追加注册由架构配置控制,以保证阶段回调的一致性。 + /// + /// 要注册的生命周期钩子。 + /// 原样返回注册的钩子实例,便于链式调用或测试断言。 + public IArchitectureLifecycleHook RegisterLifecycleHook(IArchitectureLifecycleHook hook) + { + if (CurrentPhase >= ArchitecturePhase.Ready && !configuration.ArchitectureProperties.AllowLateRegistration) + throw new InvalidOperationException("Cannot register lifecycle hook after architecture is Ready"); + + _lifecycleHooks.Add(hook); + return hook; + } + + /// + /// 进入指定阶段并广播给所有阶段消费者。 + /// 顺序保持为“更新阶段值 → 生命周期钩子 → 容器中的阶段监听器 → 外部事件”, + /// 以兼容既有调用约定。 + /// + /// 目标阶段。 + public void EnterPhase(ArchitecturePhase next) + { + ValidatePhaseTransition(next); + + var previousPhase = CurrentPhase; + CurrentPhase = next; + + if (previousPhase != next) + logger.Info($"Architecture phase changed: {previousPhase} -> {next}"); + + NotifyLifecycleHooks(next); + NotifyPhaseListeners(next); + PhaseChanged?.Invoke(next); + } + + /// + /// 根据配置验证阶段迁移是否合法。 + /// 在关闭严格校验时直接放行,以兼容对阶段流转有特殊需求的宿主。 + /// + /// 目标阶段。 + /// 当迁移不在允许集合中时抛出。 + private void ValidatePhaseTransition(ArchitecturePhase next) + { + if (!configuration.ArchitectureProperties.StrictPhaseValidation) + return; + + if (next == ArchitecturePhase.FailedInitialization) + return; + + if (ArchitectureConstants.PhaseTransitions.TryGetValue(CurrentPhase, out var allowed) && allowed.Contains(next)) + return; + + var errorMessage = $"Invalid phase transition: {CurrentPhase} -> {next}"; + logger.Fatal(errorMessage); + throw new InvalidOperationException(errorMessage); + } + + /// + /// 通知所有生命周期钩子当前阶段已变更。 + /// 生命周期钩子通常承载注册表装配等框架扩展逻辑,因此优先于普通阶段监听器执行。 + /// + /// 当前阶段。 + private void NotifyLifecycleHooks(ArchitecturePhase phase) + { + foreach (var hook in _lifecycleHooks) + { + hook.OnPhase(phase, architecture); + logger.Trace($"Notifying lifecycle hook {hook.GetType().Name} of phase {phase}"); + } + } + + /// + /// 通知容器中的阶段监听器当前阶段已变更。 + /// 这些对象通常是系统、模型或工具本身,依赖容器解析保证通知范围与运行时实例一致。 + /// + /// 当前阶段。 + private void NotifyPhaseListeners(ArchitecturePhase phase) + { + foreach (var listener in services.Container.GetAll()) + { + logger.Trace($"Notifying phase-aware object {listener.GetType().Name} of phase change to {phase}"); + listener.OnArchitecturePhase(phase); + } + } +} \ No newline at end of file diff --git a/GFramework.Core/Ioc/MicrosoftDiContainer.cs b/GFramework.Core/Ioc/MicrosoftDiContainer.cs index 48373fbf..2615ca52 100644 --- a/GFramework.Core/Ioc/MicrosoftDiContainer.cs +++ b/GFramework.Core/Ioc/MicrosoftDiContainer.cs @@ -311,7 +311,9 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null) /// /// 注册中介行为管道 - /// 用于配置Mediator框架的行为拦截和处理逻辑 + /// 用于配置Mediator框架的行为拦截和处理逻辑。 + /// 同时支持开放泛型行为类型和已闭合的具体行为类型, + /// 以兼容通用行为和针对单一请求的专用行为两种注册方式。 /// /// 行为类型,必须是引用类型 public void RegisterMediatorBehavior() where TBehavior : class @@ -321,12 +323,35 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null) { ThrowIfFrozen(); - GetServicesUnsafe.AddSingleton( - typeof(IPipelineBehavior<,>), - typeof(TBehavior) - ); + var behaviorType = typeof(TBehavior); - _logger.Debug($"Mediator behavior registered: {typeof(TBehavior).Name}"); + if (behaviorType.IsGenericTypeDefinition) + { + GetServicesUnsafe.AddSingleton(typeof(IPipelineBehavior<,>), behaviorType); + } + else + { + var pipelineInterfaces = behaviorType + .GetInterfaces() + .Where(type => type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(IPipelineBehavior<,>)) + .ToList(); + + if (pipelineInterfaces.Count == 0) + { + var errorMessage = $"{behaviorType.Name} does not implement IPipelineBehavior<,>"; + _logger.Error(errorMessage); + throw new InvalidOperationException(errorMessage); + } + + // 为每个已闭合的管道接口建立显式映射,支持针对特定请求/响应的专用行为。 + foreach (var pipelineInterface in pipelineInterfaces) + { + GetServicesUnsafe.AddSingleton(pipelineInterface, behaviorType); + } + } + + _logger.Debug($"Mediator behavior registered: {behaviorType.Name}"); } finally { From bb2b3a0c60e70f936eaf76532d14ca9a3b60f095 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:01:50 +0800 Subject: [PATCH 3/7] =?UTF-8?q?refactor(arch):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E7=BB=84=E4=BB=B6=E6=B3=A8=E5=86=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将组件注册逻辑提取到独立的 ArchitectureComponentRegistry 类中 - 实现 ArchitectureComponentActivator 来支持注册阶段实例化组件 - 更新系统和模型注册文档说明,明确在注册阶段创建实例并参与初始化 - 修复类型注册在架构准备就绪后的行为一致性问题 - 添加完整的组件注册行为测试覆盖实例注册、工厂注册和生命周期管理 - 优化依赖注入解析机制,支持构造函数依赖和单例缓存 --- ...hitectureComponentRegistryBehaviorTests.cs | 170 +++++++++++++++++- GFramework.Core/Architectures/Architecture.cs | 5 +- .../ArchitectureComponentActivator.cs | 94 ++++++++++ .../ArchitectureComponentRegistry.cs | 47 ++--- 4 files changed, 281 insertions(+), 35 deletions(-) create mode 100644 GFramework.Core/Architectures/ArchitectureComponentActivator.cs diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs index 74344191..fba37f01 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs @@ -122,6 +122,62 @@ public class ArchitectureComponentRegistryBehaviorTests await architecture.DestroyAsync(); } + /// + /// 验证系统类型注册会在初始化期间物化实例、注入构造函数依赖并执行创建回调。 + /// + [Test] + public async Task RegisterSystem_Type_Should_Create_Instance_During_Initialization() + { + var dependency = new ConstructorDependency("system-dependency"); + FactoryCreatedSystem? callbackInstance = null; + var architecture = new RegistryTestArchitecture( + target => target.RegisterSystem(created => callbackInstance = created), + services => services.AddSingleton(dependency)); + + await architecture.InitializeAsync(); + + var resolved = architecture.Context.GetSystem(); + + Assert.Multiple(() => + { + Assert.That(callbackInstance, Is.Not.Null); + Assert.That(resolved, Is.SameAs(callbackInstance)); + Assert.That(resolved.Dependency, Is.SameAs(dependency)); + Assert.That(resolved.GetContext(), Is.SameAs(architecture.Context)); + Assert.That(resolved.InitializeCallCount, Is.EqualTo(1)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证模型类型注册会在初始化期间物化实例、注入构造函数依赖并执行创建回调。 + /// + [Test] + public async Task RegisterModel_Type_Should_Create_Instance_During_Initialization() + { + var dependency = new ConstructorDependency("model-dependency"); + FactoryCreatedModel? callbackInstance = null; + var architecture = new RegistryTestArchitecture( + target => target.RegisterModel(created => callbackInstance = created), + services => services.AddSingleton(dependency)); + + await architecture.InitializeAsync(); + + var resolved = architecture.Context.GetModel(); + + Assert.Multiple(() => + { + Assert.That(callbackInstance, Is.Not.Null); + Assert.That(resolved, Is.SameAs(callbackInstance)); + Assert.That(resolved.Dependency, Is.SameAs(dependency)); + Assert.That(resolved.GetContext(), Is.SameAs(architecture.Context)); + Assert.That(resolved.InitializeCallCount, Is.EqualTo(1)); + }); + + await architecture.DestroyAsync(); + } + /// /// 验证 Ready 阶段后不允许继续注册 Utility,保持与系统和模型一致的约束。 /// @@ -142,14 +198,35 @@ public class ArchitectureComponentRegistryBehaviorTests /// /// 用于测试组件注册行为的最小架构实现。 /// - private sealed class RegistryTestArchitecture(Action registrationAction) : Architecture + private sealed class RegistryTestArchitecture : Architecture { + private readonly Action? _configurator; + private readonly Action _registrationAction; + + /// + /// 创建一个可选地附带服务配置逻辑的测试架构。 + /// + /// 初始化阶段执行的组件注册逻辑。 + /// 初始化前执行的服务配置逻辑。 + public RegistryTestArchitecture( + Action registrationAction, + Action? configurator = null) + { + _registrationAction = registrationAction; + _configurator = configurator; + } + + /// + /// 返回测试注入的服务配置逻辑。 + /// + public override Action? Configurator => _configurator; + /// /// 在初始化阶段执行测试注入的注册逻辑。 /// protected override void OnInitialize() { - registrationAction(this); + _registrationAction(this); } } @@ -303,4 +380,93 @@ public class ArchitectureComponentRegistryBehaviorTests private sealed class FactoryCreatedUtility : IUtility { } + + /// + /// 用于验证构造函数依赖注入的简单依赖对象。 + /// + private sealed class ConstructorDependency(string name) + { + /// + /// 获取依赖对象名称。 + /// + public string Name { get; } = name; + } + + /// + /// 用于验证系统类型注册路径的工厂创建系统。 + /// + private sealed class FactoryCreatedSystem(ConstructorDependency dependency) : ISystem + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的依赖对象。 + /// + public ConstructorDependency Dependency { get; } = dependency; + + /// + /// 获取初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + public void Initialize() + { + InitializeCallCount++; + } + + public void Destroy() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于验证模型类型注册路径的工厂创建模型。 + /// + private sealed class FactoryCreatedModel(ConstructorDependency dependency) : IModel + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的依赖对象。 + /// + public ConstructorDependency Dependency { get; } = dependency; + + /// + /// 获取初始化调用次数。 + /// + public int InitializeCallCount { get; private set; } + + public void Initialize() + { + InitializeCallCount++; + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } } \ No newline at end of file diff --git a/GFramework.Core/Architectures/Architecture.cs b/GFramework.Core/Architectures/Architecture.cs index af96d03f..ff12d133 100644 --- a/GFramework.Core/Architectures/Architecture.cs +++ b/GFramework.Core/Architectures/Architecture.cs @@ -7,7 +7,6 @@ using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Environment; using GFramework.Core.Logging; -using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Architectures; @@ -182,7 +181,7 @@ public abstract class Architecture : IArchitecture } /// - /// 注册系统类型,由 DI 容器自动创建实例 + /// 注册系统类型,由当前服务集合自动创建实例并接入本轮初始化 /// /// 系统类型 /// 可选的实例创建后回调 @@ -203,7 +202,7 @@ public abstract class Architecture : IArchitecture } /// - /// 注册模型类型,由 DI 容器自动创建实例 + /// 注册模型类型,由当前服务集合自动创建实例并接入本轮初始化 /// /// 模型类型 /// 可选的实例创建后回调 diff --git a/GFramework.Core/Architectures/ArchitectureComponentActivator.cs b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs new file mode 100644 index 00000000..3c2b3c80 --- /dev/null +++ b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs @@ -0,0 +1,94 @@ +using GFramework.Core.Abstractions.Ioc; +using GFramework.Core.Abstractions.Logging; + +namespace GFramework.Core.Architectures; + +/// +/// 为架构组件的类型注册路径提供实例创建能力。 +/// 该类型在容器冻结前基于当前服务集合和已注册实例进行激活, +/// 使 可以在注册阶段就物化 System / Model, +/// 避免它们在 Ready 之后首次解析时才参与生命周期而导致状态不一致。 +/// +internal sealed class ArchitectureComponentActivator( + IIocContainer container, + ILogger logger) +{ + /// + /// 根据当前容器状态创建组件实例。 + /// 激活过程优先复用已经注册到容器中的实例,再按服务描述解析实现类型或工厂方法, + /// 以兼容构造函数依赖于框架服务、用户实例服务和先前注册组件的场景。 + /// + /// 要创建的组件类型。 + /// 创建完成的组件实例。 + public TComponent CreateInstance() where TComponent : class + { + var activationProvider = new RegistrationServiceProvider(container, logger); + return ActivatorUtilities.CreateInstance(activationProvider); + } + + /// + /// 面向组件注册的轻量级服务提供者。 + /// 该实现只覆盖预冻结阶段需要的解析能力,避免引入完整容器冻结过程。 + /// + private sealed class RegistrationServiceProvider( + IIocContainer container, + ILogger logger) : IServiceProvider + { + private readonly Dictionary _singletonCache = []; + + /// + /// 从当前服务集合中解析指定类型的服务。 + /// 解析顺序为:已注册实例 → 服务描述实例/工厂/实现类型 → 可直接实例化的具体类型。 + /// + /// 请求解析的服务类型。 + /// 解析到的服务实例;若无法解析则返回 null。 + public object? GetService(Type serviceType) + { + if (serviceType == typeof(IServiceProvider)) + return this; + + var existingInstance = container.Get(serviceType); + if (existingInstance != null) + return existingInstance; + + var descriptor = + container.GetServicesUnsafe.LastOrDefault(candidate => candidate.ServiceType == serviceType); + if (descriptor != null) + return ResolveDescriptor(serviceType, descriptor); + + if (!serviceType.IsAbstract && !serviceType.IsInterface) + return ActivatorUtilities.CreateInstance(this, serviceType); + + logger.Trace($"Activation provider could not resolve {serviceType.Name}"); + return null; + } + + /// + /// 根据服务描述解析实例,并对单例描述进行缓存。 + /// 这样可以保证同一类型在一次组件注册流程中只创建一次依赖实例。 + /// + /// 请求的服务类型。 + /// 命中的服务描述。 + /// 解析到的实例。 + private object? ResolveDescriptor(Type requestedType, ServiceDescriptor descriptor) + { + if (descriptor.Lifetime == ServiceLifetime.Singleton && + _singletonCache.TryGetValue(requestedType, out var cached)) + return cached; + + object? resolved = descriptor switch + { + { ImplementationInstance: not null } => descriptor.ImplementationInstance, + { ImplementationFactory: not null } => descriptor.ImplementationFactory(this), + { ImplementationType: not null } => ActivatorUtilities.CreateInstance(this, + descriptor.ImplementationType), + _ => null + }; + + if (descriptor.Lifetime == ServiceLifetime.Singleton && resolved != null) + _singletonCache[requestedType] = resolved; + + return resolved; + } + } +} \ No newline at end of file diff --git a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs index 91b2a846..9925d533 100644 --- a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs +++ b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs @@ -5,7 +5,6 @@ using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Extensions; -using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Architectures; @@ -20,6 +19,8 @@ internal sealed class ArchitectureComponentRegistry( ArchitectureLifecycle lifecycle, ILogger logger) { + private readonly ArchitectureComponentActivator _activator = new(services.Container, logger); + #region Validation /// @@ -63,7 +64,8 @@ internal sealed class ArchitectureComponentRegistry( } /// - /// 注册系统类型,由 DI 容器自动创建实例 + /// 注册系统类型,并在注册阶段由当前服务集合立即创建实例。 + /// 这样可以确保该系统参与当前架构初始化批次,而不是等到 Ready 之后首次解析时才延迟创建。 /// /// 系统类型 /// 可选的实例创建后回调 @@ -72,21 +74,12 @@ internal sealed class ArchitectureComponentRegistry( ValidateRegistration("system"); logger.Debug($"Registering system type: {typeof(T).Name}"); - services.Container.RegisterFactory(sp => - { - // 1. DI 创建实例 - var system = ActivatorUtilities.CreateInstance(sp); - - // 2. 框架默认处理 - system.SetContext(architecture.Context); - lifecycle.RegisterLifecycleComponent(system); - - // 3. 用户自定义处理(钩子) - onCreated?.Invoke(system); - - logger.Debug($"System created: {typeof(T).Name}"); - return system; - }); + // 类型注册路径在注册阶段就物化实例,确保组件能参与当前初始化批次。 + var system = _activator.CreateInstance(); + system.SetContext(architecture.Context); + lifecycle.RegisterLifecycleComponent(system); + onCreated?.Invoke(system); + services.Container.RegisterPlurality(system); logger.Info($"System type registered: {typeof(T).Name}"); } @@ -118,7 +111,8 @@ internal sealed class ArchitectureComponentRegistry( } /// - /// 注册模型类型,由 DI 容器自动创建实例 + /// 注册模型类型,并在注册阶段由当前服务集合立即创建实例。 + /// 这样可以确保该模型参与当前架构初始化批次,而不是等到 Ready 之后首次解析时才延迟创建。 /// /// 模型类型 /// 可选的实例创建后回调 @@ -127,18 +121,11 @@ internal sealed class ArchitectureComponentRegistry( ValidateRegistration("model"); logger.Debug($"Registering model type: {typeof(T).Name}"); - services.Container.RegisterFactory(sp => - { - var model = ActivatorUtilities.CreateInstance(sp); - model.SetContext(architecture.Context); - lifecycle.RegisterLifecycleComponent(model); - - // 用户自定义钩子 - onCreated?.Invoke(model); - - logger.Debug($"Model created: {typeof(T).Name}"); - return model; - }); + var model = _activator.CreateInstance(); + model.SetContext(architecture.Context); + lifecycle.RegisterLifecycleComponent(model); + onCreated?.Invoke(model); + services.Container.RegisterPlurality(model); logger.Info($"Model type registered: {typeof(T).Name}"); } From f7e05d19ccb833d6a1a1772586e07df96d1b26ab Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:02:00 +0800 Subject: [PATCH 4/7] =?UTF-8?q?docs(core):=20=E6=B7=BB=E5=8A=A0=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E6=9E=B6=E6=9E=84=E4=B8=8ECQRS=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E8=AF=A6=E7=BB=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增Architecture架构详解文档,包含设计目标、生命周期管理和组件注册等内容 - 新增CQRS与Mediator模式文档,详细介绍命令查询职责分离的实现方式 - 新增核心框架概述文档,提供完整的架构图和快速入门指南 - 完善架构组件联动说明,涵盖初始化、命令执行和事件传播流程 - 补充最佳实践指导,明确分层职责和通信方式选择原则 --- docs/zh-CN/core/architecture.md | 117 +++++++++++++++++--------------- docs/zh-CN/core/cqrs.md | 11 ++- docs/zh-CN/core/index.md | 66 +++++++++++------- 3 files changed, 112 insertions(+), 82 deletions(-) diff --git a/docs/zh-CN/core/architecture.md b/docs/zh-CN/core/architecture.md index 90b5c341..12007e4d 100644 --- a/docs/zh-CN/core/architecture.md +++ b/docs/zh-CN/core/architecture.md @@ -14,8 +14,13 @@ ## 概述 -Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期、组件注册和模块管理。从 v1.1.0 开始,Architecture -采用模块化设计,将职责分离到专门的管理器中。 +Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期、组件注册和模块管理。从 v1.1.0 开始,Architecture +采用模块化设计,将职责分离到专门的协作者中。 + +> 命名约定: +> - `ArchitectureServices` 是公开的基础服务入口,负责容器、事件总线、命令执行器、查询执行器和服务模块管理 +> - `ArchitectureComponentRegistry` 是内部组件注册器,专门负责 System / Model / Utility 的注册与生命周期接入 +> - 两者不是同一层职责,不要混用 ### 设计目标 @@ -28,10 +33,11 @@ Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期 ### 核心组件 ``` -Architecture (核心协调器) - ├── ArchitectureLifecycle (生命周期管理) - ├── ArchitectureComponentRegistry (组件注册) - └── ArchitectureModules (模块管理) +Architecture (核心协调器) + ├── ArchitectureBootstrapper (初始化基础设施编排) + ├── ArchitectureLifecycle (生命周期管理) + ├── ArchitectureComponentRegistry (组件注册) + └── ArchitectureModules (模块管理) ``` ## 架构设计 @@ -40,18 +46,19 @@ Architecture (核心协调器) Architecture 采用以下设计模式: -1. **组合模式 (Composition)**: Architecture 组合三个管理器 -2. **委托模式 (Delegation)**: 方法调用委托给专门的管理器 -3. **协调器模式 (Coordinator)**: Architecture 作为协调器统一对外接口 +1. **组合模式 (Composition)**: Architecture 组合多个内部协作者 +2. **委托模式 (Delegation)**: 方法调用委托给专门的管理器 +3. **协调器模式 (Coordinator)**: Architecture 作为协调器统一对外接口 ### 类图 ``` ┌─────────────────────────────────────────────────────┐ -│ Architecture │ -│ - _lifecycle: ArchitectureLifecycle │ -│ - _componentRegistry: ArchitectureComponentRegistry│ -│ - _modules: ArchitectureModules │ +│ Architecture │ +│ - _bootstrapper: ArchitectureBootstrapper │ +│ - _lifecycle: ArchitectureLifecycle │ +│ - _componentRegistry: ArchitectureComponentRegistry│ +│ - _modules: ArchitectureModules │ │ - _logger: ILogger │ │ │ │ + RegisterSystem() │ @@ -62,17 +69,17 @@ Architecture 采用以下设计模式: │ + DestroyAsync() │ │ + event PhaseChanged │ └─────────────────────────────────────────────────────┘ - │ │ │ - │ │ │ - ▼ ▼ ▼ -┌──────────────┐ ┌──────────────────┐ ┌──────────────┐ -│ Lifecycle │ │ ComponentRegistry│ │ Modules │ -│ │ │ │ │ │ -│ - 阶段管理 │ │ - System 注册 │ │ - 模块安装 │ -│ - 钩子管理 │ │ - Model 注册 │ │ - 行为注册 │ -│ - 初始化 │ │ - Utility 注册 │ │ │ -│ - 销毁 │ │ - 生命周期注册 │ │ │ -└──────────────┘ └──────────────────┘ └──────────────┘ + │ │ │ │ + │ │ │ │ + ▼ ▼ ▼ ▼ +┌──────────────┐ ┌──────────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Bootstrapper │ │ Lifecycle │ │ComponentReg. │ │ Modules │ +│ │ │ │ │ │ │ │ +│ - 环境初始化 │ │ - 阶段管理 │ │ - System 注册│ │ - 模块安装 │ +│ - 服务准备 │ │ - 钩子管理 │ │ - Model 注册 │ │ - 行为注册 │ +│ - 上下文绑定 │ │ - 组件初始化 │ │ - Utility 注册│ │ │ +│ - 容器冻结 │ │ - 就绪/销毁协调 │ │ - 生命周期接入│ │ │ +└──────────────┘ └──────────────────┘ └──────────────┘ └──────────────┘ ``` ### 构造函数初始化 @@ -80,27 +87,28 @@ Architecture 采用以下设计模式: 从 v1.1.0 开始,所有管理器在构造函数中初始化: ```csharp -protected Architecture( - IArchitectureConfiguration? configuration = null, - IEnvironment? environment = null, - IArchitectureServices? services = null, - IArchitectureContext? context = null) -{ - Configuration = configuration ?? new ArchitectureConfiguration(); - Environment = environment ?? new DefaultEnvironment(); - Services = services ?? new ArchitectureServices(); - _context = context; - - // 初始化 Logger - LoggerFactoryResolver.Provider = Configuration.LoggerProperties.LoggerFactoryProvider; - _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); - - // 初始化管理器 - _lifecycle = new ArchitectureLifecycle(this, Configuration, Services, _logger); - _componentRegistry = new ArchitectureComponentRegistry(this, Configuration, Services, _lifecycle, _logger); - _modules = new ArchitectureModules(this, Services, _logger); -} -``` +protected Architecture( + IArchitectureConfiguration? configuration = null, + IEnvironment? environment = null, + IArchitectureServices? services = null, + IArchitectureContext? context = null) +{ + var resolvedConfiguration = configuration ?? new ArchitectureConfiguration(); + var resolvedEnvironment = environment ?? new DefaultEnvironment(); + var resolvedServices = services ?? new ArchitectureServices(); + _context = context; + + // 初始化 Logger + LoggerFactoryResolver.Provider = resolvedConfiguration.LoggerProperties.LoggerFactoryProvider; + _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); + + // 初始化协作者 + _bootstrapper = new ArchitectureBootstrapper(GetType(), resolvedEnvironment, resolvedServices, _logger); + _lifecycle = new ArchitectureLifecycle(this, resolvedConfiguration, resolvedServices, _logger); + _componentRegistry = new ArchitectureComponentRegistry(this, resolvedConfiguration, resolvedServices, _lifecycle, _logger); + _modules = new ArchitectureModules(this, resolvedServices, _logger); +} +``` **优势**: @@ -189,15 +197,16 @@ architecture.RegisterLifecycleHook(new MyLifecycleHook()); 1. 创建 Architecture 实例 └─> 构造函数初始化管理器 -2. 调用 InitializeAsync() 或 Initialize() - ├─> 初始化环境 (Environment.Initialize()) - ├─> 注册内置服务模块 - ├─> 初始化架构上下文 - ├─> 执行服务钩子 - ├─> 初始化服务模块 - ├─> 调用 OnInitialize() (用户注册组件) - ├─> 初始化所有组件 - │ ├─> BeforeUtilityInit → 初始化 Utility → AfterUtilityInit +2. 调用 InitializeAsync() 或 Initialize() + ├─> ArchitectureBootstrapper 准备基础设施 + │ ├─> 初始化环境 (Environment.Initialize()) + │ ├─> 注册内置服务模块 + │ ├─> 初始化架构上下文并绑定 GameContext + │ ├─> 执行服务钩子 + │ └─> 初始化服务模块 + ├─> 调用 OnInitialize() (用户注册组件) + ├─> 初始化所有组件 + │ ├─> BeforeUtilityInit → 初始化 Utility → AfterUtilityInit │ ├─> BeforeModelInit → 初始化 Model → AfterModelInit │ └─> BeforeSystemInit → 初始化 System → AfterSystemInit ├─> 冻结 IoC 容器 diff --git a/docs/zh-CN/core/cqrs.md b/docs/zh-CN/core/cqrs.md index f1f3801a..d0cf0f9e 100644 --- a/docs/zh-CN/core/cqrs.md +++ b/docs/zh-CN/core/cqrs.md @@ -216,15 +216,20 @@ public class GameArchitecture : Architecture { protected override void Init() { - // 注册 Mediator 行为 - RegisterMediatorBehavior(); - RegisterMediatorBehavior(); + // 注册通用开放泛型行为 + RegisterMediatorBehavior>(); + RegisterMediatorBehavior>(); // 处理器会自动通过依赖注入注册 } } ``` +`RegisterMediatorBehavior()` 同时支持两种形式: + +- 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求 +- 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior` + ## 高级用法 ### Request(请求) diff --git a/docs/zh-CN/core/index.md b/docs/zh-CN/core/index.md index a61cfb37..950c7100 100644 --- a/docs/zh-CN/core/index.md +++ b/docs/zh-CN/core/index.md @@ -319,7 +319,7 @@ public class PlayerController : IController ### Architecture 内部结构 (v1.1.0+) -从 v1.1.0 开始,Architecture 类采用模块化设计,将原本 708 行的单一类拆分为 4 个职责清晰的类: +从 v1.1.0 开始,Architecture 类采用模块化设计,将原本 708 行的单一类拆分为多个职责清晰的协作者: #### 1. Architecture (核心协调器) @@ -338,10 +338,21 @@ public class PlayerController : IController - `PhaseChanged` - 阶段变更事件 -#### 2. ArchitectureLifecycle (生命周期管理器) - -**职责**: 管理架构的生命周期和阶段转换 - +#### 2. ArchitectureBootstrapper (初始化基础设施编排器) + +**职责**: 在用户 `OnInitialize()` 执行前准备环境、服务和上下文 + +**核心功能**: + +- 初始化环境对象 +- 注册内置服务模块 +- 绑定架构上下文到 `GameContext` +- 执行服务钩子并冻结 IoC 容器 + +#### 3. ArchitectureLifecycle (生命周期管理器) + +**职责**: 管理架构的生命周期和阶段转换 + **核心功能**: - 11 个架构阶段的管理和转换 @@ -355,11 +366,11 @@ public class PlayerController : IController - `EnterPhase()` - 进入指定阶段 - `RegisterLifecycleHook()` - 注册生命周期钩子 - `InitializeAllComponentsAsync()` - 初始化所有组件 -- `DestroyAsync()` - 异步销毁 - -#### 3. ArchitectureComponentRegistry (组件注册管理器) - -**职责**: 管理 System、Model、Utility 的注册 +- `DestroyAsync()` - 异步销毁 + +#### 4. ArchitectureComponentRegistry (组件注册管理器) + +**职责**: 管理 System、Model、Utility 的注册 **核心功能**: @@ -370,11 +381,14 @@ public class PlayerController : IController **关键方法**: -- `RegisterSystem()` - 注册系统 -- `RegisterModel()` - 注册模型 -- `RegisterUtility()` - 注册工具 - -#### 4. ArchitectureModules (模块管理器) +- `RegisterSystem()` - 注册系统 +- `RegisterModel()` - 注册模型 +- `RegisterUtility()` - 注册工具 + +> 命名提醒: 公开的 `ArchitectureServices` 负责容器和基础服务,并不承担组件注册职责。 +> `ArchitectureComponentRegistry` 才是内部的 System / Model / Utility 注册器。 + +#### 5. ArchitectureModules (模块管理器) **职责**: 管理架构模块和中介行为 @@ -429,16 +443,18 @@ public class PlayerController : IController ### 1. 初始化流程 ``` -创建 Architecture 实例 - └─> 构造函数 - ├─> 初始化 Logger - ├─> 创建 ArchitectureLifecycle - ├─> 创建 ArchitectureComponentRegistry - └─> 创建 ArchitectureModules - └─> InitializeAsync() - ├─> OnInitialize() (用户注册组件) - │ ├─> RegisterModel → Model.SetContext() - │ ├─> RegisterSystem → System.SetContext() +创建 Architecture 实例 + └─> 构造函数 + ├─> 初始化 Logger + ├─> 创建 ArchitectureBootstrapper + ├─> 创建 ArchitectureLifecycle + ├─> 创建 ArchitectureComponentRegistry + └─> 创建 ArchitectureModules + └─> InitializeAsync() + ├─> Bootstrapper 准备环境/服务/上下文 + ├─> OnInitialize() (用户注册组件) + │ ├─> RegisterModel → Model.SetContext() + │ ├─> RegisterSystem → System.SetContext() │ └─> RegisterUtility → 注册到容器 └─> InitializeAllComponentsAsync() ├─> BeforeUtilityInit → Utility.Initialize() From 8f6c453c1aa8e52e93a1f2ceb35d343811228723 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:18:42 +0800 Subject: [PATCH 5/7] =?UTF-8?q?refactor(architectures):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 Architecture.cs 中引入 Microsoft.Extensions.DependencyInjection 命名空间 - 在 ArchitectureComponentActivator.cs 中引入 Microsoft.Extensions.DependencyInjection 命名空间 - 为架构组件激活器增加依赖注入支持 - 统一架构层的服务注册机制 --- GFramework.Core/Architectures/Architecture.cs | 1 + GFramework.Core/Architectures/ArchitectureComponentActivator.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/GFramework.Core/Architectures/Architecture.cs b/GFramework.Core/Architectures/Architecture.cs index ff12d133..39b4fd13 100644 --- a/GFramework.Core/Architectures/Architecture.cs +++ b/GFramework.Core/Architectures/Architecture.cs @@ -7,6 +7,7 @@ using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Environment; using GFramework.Core.Logging; +using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Architectures; diff --git a/GFramework.Core/Architectures/ArchitectureComponentActivator.cs b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs index 3c2b3c80..88afa810 100644 --- a/GFramework.Core/Architectures/ArchitectureComponentActivator.cs +++ b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs @@ -1,5 +1,6 @@ using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Logging; +using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Architectures; From 4ad89b10b2da1ed7e4583416c98df4821181d581 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:28:32 +0800 Subject: [PATCH 6/7] =?UTF-8?q?chore(dependencies):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20Microsoft.Extensions.DependencyInjection=20=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ArchitectureComponentRegistry.cs 中添加 Microsoft.Extensions.DependencyInjection 引用 - 在 ArchitectureComponentRegistryBehaviorTests.cs 中添加 Microsoft.Extensions.DependencyInjection 引用 --- .../Architectures/ArchitectureComponentRegistryBehaviorTests.cs | 1 + GFramework.Core/Architectures/ArchitectureComponentRegistry.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs index fba37f01..6cd98bc2 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs @@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; using GFramework.Core.Logging; +using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Tests.Architectures; diff --git a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs index 9925d533..63810c28 100644 --- a/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs +++ b/GFramework.Core/Architectures/ArchitectureComponentRegistry.cs @@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Extensions; +using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Architectures; From e3ea364b29e8744cdfa82205f13aacfe382bb91b Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 6 Apr 2026 08:33:56 +0800 Subject: [PATCH 7/7] =?UTF-8?q?docs(core):=20=E6=B7=BB=E5=8A=A0=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E8=AF=A6=E8=A7=A3=E5=92=8C=E6=A0=B8=E5=BF=83=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Architecture 架构详解文档,涵盖设计目标、核心组件、生命周期管理 - 添加 GFramework.Core 核心框架文档,包含五层架构、快速开始、组件联动等内容 - 新增架构组件激活器实现,支持类型注册路径的实例创建能力 - 添加架构销毁器,统一处理可销毁对象的登记与释放 - 实现架构生命周期管理器,负责阶段转换和组件初始化销毁 --- ...hitectureComponentRegistryBehaviorTests.cs | 244 ++++++++++++++++++ .../ArchitectureLifecycleBehaviorTests.cs | 210 +++++++++++++++ .../ArchitectureComponentActivator.cs | 17 +- .../Architectures/ArchitectureDisposer.cs | 5 +- .../Architectures/ArchitectureLifecycle.cs | 21 +- docs/zh-CN/core/architecture.md | 28 +- docs/zh-CN/core/index.md | 20 +- 7 files changed, 515 insertions(+), 30 deletions(-) diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs index 6cd98bc2..583c30c8 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs @@ -179,6 +179,77 @@ public class ArchitectureComponentRegistryBehaviorTests await architecture.DestroyAsync(); } + /// + /// 验证预冻结阶段通过实现类型注册的单例依赖会在同一轮组件激活中复用同一个实例。 + /// 该回归测试用于保护 的共享单例缓存,避免系统和模型分别创建重复单例。 + /// + [Test] + public async Task RegisterSystem_And_Model_Type_Should_Reuse_ImplementationType_Singleton_During_Activation() + { + var counter = new DependencyConstructionCounter(); + var architecture = new RegistryTestArchitecture( + target => + { + target.RegisterSystem(); + target.RegisterModel(); + }, + services => + { + services.AddSingleton(counter); + services.AddSingleton(); + }); + + await architecture.InitializeAsync(); + + var system = architecture.Context.GetSystem(); + var model = architecture.Context.GetModel(); + + Assert.Multiple(() => + { + Assert.That(counter.CreationCount, Is.EqualTo(1)); + Assert.That(system.Dependency, Is.SameAs(model.Dependency)); + }); + + await architecture.DestroyAsync(); + } + + /// + /// 验证预冻结阶段通过工厂注册的单例依赖会在同一轮组件激活中复用同一个实例。 + /// 该回归测试覆盖 ImplementationFactory 描述符路径,避免用户工厂在初始化时被重复调用。 + /// + [Test] + public async Task RegisterSystem_And_Model_Type_Should_Reuse_Factory_Singleton_During_Activation() + { + var creationCount = 0; + var architecture = new RegistryTestArchitecture( + target => + { + target.RegisterSystem(); + target.RegisterModel(); + }, + services => + { + services.AddSingleton(_ => + { + creationCount++; + return new FactorySharedDependency(); + }); + }); + + await architecture.InitializeAsync(); + + var system = architecture.Context.GetSystem(); + var model = architecture.Context.GetModel(); + + Assert.Multiple(() => + { + Assert.That(creationCount, Is.EqualTo(1)); + Assert.That(system.Dependency, Is.SameAs(model.Dependency)); + }); + + await architecture.DestroyAsync(); + } + /// /// 验证 Ready 阶段后不允许继续注册 Utility,保持与系统和模型一致的约束。 /// @@ -470,4 +541,177 @@ public class ArchitectureComponentRegistryBehaviorTests return _context; } } + + /// + /// 统计实现类型单例在预冻结激活阶段的构造次数。 + /// + private sealed class DependencyConstructionCounter + { + /// + /// 获取共享依赖被构造的次数。 + /// + public int CreationCount { get; private set; } + + /// + /// 记录一次新的依赖构造。 + /// + public void RecordCreation() + { + CreationCount++; + } + } + + /// + /// 用于覆盖 ImplementationType 单例描述符路径的共享依赖。 + /// + private sealed class ImplementationTypeSharedDependency + { + /// + /// 创建共享依赖并记录构造次数。 + /// + /// 用于统计构造次数的计数器。 + public ImplementationTypeSharedDependency(DependencyConstructionCounter counter) + { + counter.RecordCreation(); + } + } + + /// + /// 用于覆盖 ImplementationType 单例复用路径的测试系统。 + /// + private sealed class ImplementationTypeDependencySystem(ImplementationTypeSharedDependency dependency) : ISystem + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的共享依赖。 + /// + public ImplementationTypeSharedDependency Dependency { get; } = dependency; + + public void Initialize() + { + } + + public void Destroy() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于覆盖 ImplementationType 单例复用路径的测试模型。 + /// + private sealed class ImplementationTypeDependencyModel(ImplementationTypeSharedDependency dependency) : IModel + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的共享依赖。 + /// + public ImplementationTypeSharedDependency Dependency { get; } = dependency; + + public void Initialize() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于覆盖 ImplementationFactory 单例描述符路径的共享依赖。 + /// + private sealed class FactorySharedDependency + { + } + + /// + /// 用于覆盖 ImplementationFactory 单例复用路径的测试系统。 + /// + private sealed class FactoryDependencySystem(FactorySharedDependency dependency) : ISystem + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的共享依赖。 + /// + public FactorySharedDependency Dependency { get; } = dependency; + + public void Initialize() + { + } + + public void Destroy() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } + + /// + /// 用于覆盖 ImplementationFactory 单例复用路径的测试模型。 + /// + private sealed class FactoryDependencyModel(FactorySharedDependency dependency) : IModel + { + private IArchitectureContext _context = null!; + + /// + /// 获取构造函数注入的共享依赖。 + /// + public FactorySharedDependency Dependency { get; } = dependency; + + public void Initialize() + { + } + + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + public IArchitectureContext GetContext() + { + return _context; + } + } } \ No newline at end of file diff --git a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs index 0bde4b5d..b0ef7262 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs @@ -1,3 +1,4 @@ +using System.Reflection; using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Lifecycle; @@ -6,6 +7,7 @@ using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; using GFramework.Core.Logging; +using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Tests.Architectures; @@ -98,6 +100,75 @@ public class ArchitectureLifecycleBehaviorTests }); } + /// + /// 验证初始化失败后仍然允许执行销毁流程。 + /// 该回归测试用于保护 FailedInitialization → Destroying 的合法迁移,避免失败路径上的组件与容器泄漏。 + /// + [Test] + public async Task DestroyAsync_After_FailedInitialization_Should_Cleanup_And_Enter_Destroyed() + { + var destroyOrder = new List(); + var architecture = new FailingInitializationArchitecture(destroyOrder); + + var exception = Assert.ThrowsAsync(async () => await architecture.InitializeAsync()); + Assert.That(exception, Is.Not.Null); + Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.FailedInitialization)); + + await architecture.DestroyAsync(); + + Assert.Multiple(() => + { + Assert.That(destroyOrder, Is.EqualTo(new[] { "model", "utility" })); + Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.Destroyed)); + Assert.That(architecture.PhaseHistory[^3..], Is.EqualTo(new[] + { + ArchitecturePhase.FailedInitialization, + ArchitecturePhase.Destroying, + ArchitecturePhase.Destroyed + })); + }); + } + + /// + /// 验证 Destroyed 阶段会在容器清空前广播给容器内的阶段监听器。 + /// 该回归测试保护销毁尾声的阶段通知,确保依赖最终阶段信号的服务仍能收到 Destroyed。 + /// + [Test] + public async Task DestroyAsync_Should_Notify_Container_Phase_Listeners_About_Destroyed_Before_Clear() + { + var listener = new TrackingPhaseListener(); + var architecture = new ListenerTrackingArchitecture(listener); + + await architecture.InitializeAsync(); + await architecture.DestroyAsync(); + + Assert.That(listener.ObservedPhases[^2..], Is.EqualTo(new[] + { + ArchitecturePhase.Destroying, + ArchitecturePhase.Destroyed + })); + } + + /// + /// 验证启用 AllowLateRegistration 时,生命周期层会立即初始化后注册的组件,而不是继续沿用初始化期的拒绝策略。 + /// 由于公共架构 API 在 Ready 之后会先触发容器限制,此回归测试直接覆盖生命周期协作者的对齐逻辑。 + /// + [Test] + public async Task + RegisterLifecycleComponent_After_Initialization_Should_Initialize_Immediately_When_LateRegistration_Is_Enabled() + { + var architecture = new AllowLateRegistrationArchitecture(); + await architecture.InitializeAsync(); + + var lateComponent = new LateRegisteredInitializableComponent(); + + architecture.RegisterLateComponentForTesting(lateComponent); + + Assert.That(lateComponent.InitializeCallCount, Is.EqualTo(1)); + + await architecture.DestroyAsync(); + } + /// /// 记录阶段流转的可配置测试架构。 /// @@ -162,6 +233,106 @@ public class ArchitectureLifecycleBehaviorTests } } + /// + /// 在初始化阶段注册可销毁组件并随后抛出异常的测试架构。 + /// + private sealed class FailingInitializationArchitecture : Architecture + { + private readonly List _destroyOrder; + + /// + /// 创建用于验证失败后销毁行为的测试架构。 + /// + /// 记录失败后清理顺序的列表。 + public FailingInitializationArchitecture(List destroyOrder) + { + _destroyOrder = destroyOrder; + PhaseChanged += phase => PhaseHistory.Add(phase); + } + + /// + /// 获取架构经历过的阶段列表。 + /// + public List PhaseHistory { get; } = []; + + /// + /// 注册可销毁组件后故意抛出异常,模拟初始化失败场景。 + /// + protected override void OnInitialize() + { + RegisterUtility(new TrackingDestroyableUtility(_destroyOrder)); + RegisterModel(new TrackingDestroyableModel(_destroyOrder)); + throw new InvalidOperationException("boom"); + } + } + + /// + /// 通过配置器把阶段监听器注册到容器中的测试架构。 + /// + private sealed class ListenerTrackingArchitecture(TrackingPhaseListener listener) : Architecture + { + /// + /// 保持对监听器的引用,以便配置器在初始化前把同一实例注册到容器。 + /// + public override Action? Configurator => + services => services.AddSingleton(listener); + + /// + /// 该测试不需要额外组件注册。 + /// + protected override void OnInitialize() + { + } + } + + /// + /// 启用 AllowLateRegistration 的测试架构。 + /// 该架构暴露生命周期协作者供回归测试验证内部注册策略对齐。 + /// + private sealed class AllowLateRegistrationArchitecture : Architecture + { + /// + /// 使用允许后注册的配置创建测试架构。 + /// + public AllowLateRegistrationArchitecture() + : base(new ArchitectureConfiguration + { + ArchitectureProperties = new() + { + AllowLateRegistration = true, + StrictPhaseValidation = true + } + }) + { + } + + /// + /// 该测试不需要初始组件。 + /// + protected override void OnInitialize() + { + } + + /// + /// 通过反射调用内部生命周期协作者的注册逻辑,以便覆盖无法通过公共 API 直接到达的后注册初始化路径。 + /// + /// 要登记到生命周期中的后注册组件。 + public void RegisterLateComponentForTesting(object component) + { + var field = typeof(Architecture).GetField( + "_lifecycle", + BindingFlags.Instance | BindingFlags.NonPublic); + var lifecycle = field?.GetValue(this) ?? + throw new InvalidOperationException("Architecture lifecycle field was not found."); + var registerMethod = lifecycle.GetType().GetMethod(nameof(RegisterLateComponentForTesting)) ?? + lifecycle.GetType().GetMethod("RegisterLifecycleComponent") ?? + throw new InvalidOperationException( + "Architecture lifecycle registration method was not found."); + + registerMethod.Invoke(lifecycle, [component]); + } + } + /// /// 用于验证逆序销毁的上下文工具。 /// @@ -189,6 +360,45 @@ public class ArchitectureLifecycleBehaviorTests } } + /// + /// 记录容器阶段通知顺序的监听器。 + /// + private sealed class TrackingPhaseListener : IArchitecturePhaseListener + { + /// + /// 获取监听到的阶段列表。 + /// + public List ObservedPhases { get; } = []; + + /// + /// 记录收到的阶段通知。 + /// + /// 当前阶段。 + public void OnArchitecturePhase(ArchitecturePhase phase) + { + ObservedPhases.Add(phase); + } + } + + /// + /// 记录即时初始化次数的后注册测试组件。 + /// + private sealed class LateRegisteredInitializableComponent : IInitializable + { + /// + /// 获取组件被即时初始化的次数。 + /// + public int InitializeCallCount { get; private set; } + + /// + /// 记录一次初始化调用。 + /// + public void Initialize() + { + InitializeCallCount++; + } + } + /// /// 用于验证逆序销毁的模型。 /// diff --git a/GFramework.Core/Architectures/ArchitectureComponentActivator.cs b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs index 88afa810..0bf121e5 100644 --- a/GFramework.Core/Architectures/ArchitectureComponentActivator.cs +++ b/GFramework.Core/Architectures/ArchitectureComponentActivator.cs @@ -14,6 +14,12 @@ internal sealed class ArchitectureComponentActivator( IIocContainer container, ILogger logger) { + /// + /// 预冻结阶段的单例实例缓存。 + /// 该缓存跨越整个架构组件激活周期共享,确保多个组件在同一轮初始化中解析到同一个单例描述时不会重复创建实例。 + /// + private readonly Dictionary _singletonCache = []; + /// /// 根据当前容器状态创建组件实例。 /// 激活过程优先复用已经注册到容器中的实例,再按服务描述解析实现类型或工厂方法, @@ -23,7 +29,7 @@ internal sealed class ArchitectureComponentActivator( /// 创建完成的组件实例。 public TComponent CreateInstance() where TComponent : class { - var activationProvider = new RegistrationServiceProvider(container, logger); + var activationProvider = new RegistrationServiceProvider(container, logger, _singletonCache); return ActivatorUtilities.CreateInstance(activationProvider); } @@ -33,9 +39,14 @@ internal sealed class ArchitectureComponentActivator( /// private sealed class RegistrationServiceProvider( IIocContainer container, - ILogger logger) : IServiceProvider + ILogger logger, + Dictionary singletonCache) : IServiceProvider { - private readonly Dictionary _singletonCache = []; + /// + /// 共享的单例缓存。 + /// 该缓存由外层 activator 统一持有,从而把单例复用范围提升到整个组件注册批次,而不是单次实例创建调用。 + /// + private readonly Dictionary _singletonCache = singletonCache; /// /// 从当前服务集合中解析指定类型的服务。 diff --git a/GFramework.Core/Architectures/ArchitectureDisposer.cs b/GFramework.Core/Architectures/ArchitectureDisposer.cs index 1c1a861d..c2c7f553 100644 --- a/GFramework.Core/Architectures/ArchitectureDisposer.cs +++ b/GFramework.Core/Architectures/ArchitectureDisposer.cs @@ -50,7 +50,7 @@ internal sealed class ArchitectureDisposer( /// 用于推进架构阶段的回调。 public async ValueTask DestroyAsync(ArchitecturePhase currentPhase, Action enterPhase) { - if (currentPhase >= ArchitecturePhase.Destroying) + if (currentPhase is ArchitecturePhase.Destroying or ArchitecturePhase.Destroyed) { logger.Warn("Architecture destroy called but already in destroying/destroyed state"); return; @@ -68,9 +68,10 @@ internal sealed class ArchitectureDisposer( await CleanupComponentsAsync(); await services.ModuleManager.DestroyAllAsync(); - services.Container.Clear(); + // Destroyed 广播依赖容器中的阶段监听器,必须在清空容器前完成。 enterPhase(ArchitecturePhase.Destroyed); + services.Container.Clear(); logger.Info("Architecture destruction completed"); } diff --git a/GFramework.Core/Architectures/ArchitectureLifecycle.cs b/GFramework.Core/Architectures/ArchitectureLifecycle.cs index 85c06ed7..6cb8f3df 100644 --- a/GFramework.Core/Architectures/ArchitectureLifecycle.cs +++ b/GFramework.Core/Architectures/ArchitectureLifecycle.cs @@ -43,9 +43,14 @@ internal sealed class ArchitectureLifecycle( if (component is IInitializable initializable) { if (_initialized) - throw new InvalidOperationException("Cannot initialize component after Architecture is Ready"); + { + if (!configuration.ArchitectureProperties.AllowLateRegistration) + throw new InvalidOperationException("Cannot initialize component after Architecture is Ready"); - if (_pendingInitializableSet.Add(initializable)) + InitializeLateRegisteredComponent(initializable); + } + + else if (_pendingInitializableSet.Add(initializable)) { _pendingInitializableList.Add(initializable); logger.Trace($"Added {component.GetType().Name} to pending initialization queue"); @@ -218,6 +223,18 @@ internal sealed class ArchitectureLifecycle( component.Initialize(); } + /// + /// 立即初始化在常规初始化批次完成后新增的组件。 + /// 当启用 AllowLateRegistration 时,生命周期层需要和注册层保持一致, + /// 让新增组件在注册当下完成同步初始化,而不是停留在未初始化状态。 + /// + /// 后注册的可初始化组件。 + private void InitializeLateRegisteredComponent(IInitializable component) + { + logger.Debug($"Initializing late-registered component: {component.GetType().Name}"); + component.Initialize(); + } + #endregion #region Destruction diff --git a/docs/zh-CN/core/architecture.md b/docs/zh-CN/core/architecture.md index 12007e4d..066dc7d6 100644 --- a/docs/zh-CN/core/architecture.md +++ b/docs/zh-CN/core/architecture.md @@ -207,10 +207,10 @@ architecture.RegisterLifecycleHook(new MyLifecycleHook()); ├─> 调用 OnInitialize() (用户注册组件) ├─> 初始化所有组件 │ ├─> BeforeUtilityInit → 初始化 Utility → AfterUtilityInit - │ ├─> BeforeModelInit → 初始化 Model → AfterModelInit - │ └─> BeforeSystemInit → 初始化 System → AfterSystemInit - ├─> 冻结 IoC 容器 - └─> 进入 Ready 阶段 + │ ├─> BeforeModelInit → 初始化 Model → AfterModelInit + │ └─> BeforeSystemInit → 初始化 System → AfterSystemInit + ├─> CompleteInitialization() 冻结 IoC 容器 + └─> 进入 Ready 阶段 3. 等待就绪 (可选) └─> await architecture.WaitUntilReadyAsync() @@ -219,16 +219,16 @@ architecture.RegisterLifecycleHook(new MyLifecycleHook()); ### 销毁流程 ``` -1. 调用 DestroyAsync() 或 Destroy() - ├─> 检查当前阶段 (如果是 None 或已销毁则直接返回) - ├─> 进入 Destroying 阶段 - ├─> 逆序销毁所有组件 - │ ├─> 优先调用 IAsyncDestroyable.DestroyAsync() - │ └─> 否则调用 IDestroyable.Destroy() - ├─> 销毁服务模块 - ├─> 清空 IoC 容器 - └─> 进入 Destroyed 阶段 -``` +1. 调用 DestroyAsync() 或 Destroy() + ├─> 检查当前阶段 (如果是 None 或已销毁则直接返回) + ├─> 进入 Destroying 阶段 + ├─> 逆序销毁所有组件 + │ ├─> 优先调用 IAsyncDestroyable.DestroyAsync() + │ └─> 否则调用 IDestroyable.Destroy() + ├─> 销毁服务模块 + ├─> 进入 Destroyed 阶段 + └─> 清空 IoC 容器 +``` --- diff --git a/docs/zh-CN/core/index.md b/docs/zh-CN/core/index.md index 950c7100..48563aea 100644 --- a/docs/zh-CN/core/index.md +++ b/docs/zh-CN/core/index.md @@ -340,14 +340,15 @@ public class PlayerController : IController #### 2. ArchitectureBootstrapper (初始化基础设施编排器) -**职责**: 在用户 `OnInitialize()` 执行前准备环境、服务和上下文 +**职责**: 在用户 `OnInitialize()` 执行前准备环境、服务和上下文,并在组件初始化完成后执行初始化收尾 **核心功能**: - 初始化环境对象 - 注册内置服务模块 - 绑定架构上下文到 `GameContext` -- 执行服务钩子并冻结 IoC 容器 +- 执行服务钩子 +- 在 `InitializeAllComponentsAsync()` 完成后通过 `CompleteInitialization()` 冻结 IoC 容器 #### 3. ArchitectureLifecycle (生命周期管理器) @@ -455,13 +456,14 @@ public class PlayerController : IController ├─> OnInitialize() (用户注册组件) │ ├─> RegisterModel → Model.SetContext() │ ├─> RegisterSystem → System.SetContext() - │ └─> RegisterUtility → 注册到容器 - └─> InitializeAllComponentsAsync() - ├─> BeforeUtilityInit → Utility.Initialize() - ├─> BeforeModelInit → Model.Initialize() - ├─> BeforeSystemInit → System.Initialize() - └─> Ready -``` + │ └─> RegisterUtility → 注册到容器 + ├─> InitializeAllComponentsAsync() + │ ├─> BeforeUtilityInit → Utility.Initialize() + │ ├─> BeforeModelInit → Model.Initialize() + │ └─> BeforeSystemInit → System.Initialize() + ├─> CompleteInitialization() → 冻结 IoC 容器 + └─> 进入 Ready +``` **重要变更 (v1.1.0)**: 管理器现在在构造函数中初始化,而不是在 InitializeAsync 中。这消除了 `null!` 断言,提高了代码安全性。