You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring4D依赖注入结合MVP架构时循环创建实例问题求助

Spring4D 解决MVP循环依赖问题

问题原因分析

你当前的代码存在循环依赖死循环:

  1. 视图在FormCreate中主动解析IConversorService(Presenter)
  2. Presenter的构造函数依赖注入IConversorView(视图)
  3. 虽然视图注册了单例,但如果视图是手动通过Application.CreateForm创建的,容器中的单例实例和当前运行的视图实例是两个独立对象。容器解析Presenter时,会创建新的视图单例,新视图又会触发解析Presenter,最终陷入循环。
  4. 即便视图由容器创建,构造函数注入的循环依赖也会导致容器无法完成对象初始化——创建Presenter需要视图,创建视图需要Presenter,形成死锁。

Spring4D完全支持这种MVP场景,只需调整依赖注入的方式或实例管理逻辑即可解决。

解决方案

方案1:属性注入替代构造函数注入(推荐)

通过属性注入避免构造函数层面的循环依赖,让容器在对象创建后完成依赖注入:

1. 修改Presenter代码

Type
  TConversorService = Class(TInterfacedObject, IConversorService)
  Private
    [Inject] // 标记为需要注入的属性
    FView: IConversorView;
    [Inject]
    FUseCase: IConversorUseCase;
  Public
    Constructor Create; // 移除构造函数的依赖参数
  End;

implementation

Constructor TConversorService.Create;
begin
  inherited;
  // 容器会在实例创建后自动注入FView和FUseCase
end;

2. 修改视图代码

type
  TfrmConversorView = class(TForm, IConversorView)
    JvStatusBar1: TJvStatusBar;
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    [Inject] // 标记为需要注入的属性
    FService: IConversorService;
  public
  end;

...
procedure TfrmConversorView.FormCreate(Sender: TObject);
begin
  // 移除手动Resolve,调用容器完成属性注入
  GlobalContainer.Inject(Self);
end;

3. 调整容器注册

procedure RegisterTypes(const Container: TContainer);
begin
  Container.RegisterType<TConexao>.asSingleton;
  // 显式声明实现的接口,提升代码可读性
  Container.RegisterType<TConversorService>.Implements<IConversorService>;
  Container.RegisterType<TfrmConversorView>.AsSingleton.Implements<IConversorView>;
  Container.RegisterType<TConversorUseCase>.Implements<IConversorUseCase>;

  container.Build;
end;

方案2:手动注册已创建的视图实例

如果必须保留构造函数注入,且视图是手动创建的,需将已创建的视图实例注册到容器,确保容器解析IConversorView时使用现有实例:

1. 修改视图的FormCreate

procedure TfrmConversorView.FormCreate(Sender: TObject);
begin
  // 将当前视图实例注册为单例,覆盖之前的类型注册
  GlobalContainer.RegisterInstance<IConversorView>(Self).AsSingleton;
  // 再解析Presenter
  FService := GlobalContainer.Resolve<IConversorService>;
end;

2. 调整容器注册

procedure RegisterTypes(const Container: TContainer);
begin
  Container.RegisterType<TConexao>.asSingleton;
  Container.RegisterType<TConversorService>.Implements<IConversorService>;
  // 移除视图的类型注册,避免冲突
  // Container.RegisterType<TfrmConversorView>.AsSingleton();
  Container.RegisterType<TConversorUseCase>.Implements<IConversorUseCase>;

  container.Build;
end;

方案3:由容器创建视图实例

让容器负责创建视图,替代手动Application.CreateForm,容器可自动跟踪实例并处理依赖:

1. 程序入口修改

program ConversorApp;
uses
  Vcl.Forms,
  Spring.Container,
  YourUnit in 'YourUnit.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  
  RegisterTypes(GlobalContainer);
  // 由容器解析视图实例,自动完成依赖注入
  Application.MainForm := GlobalContainer.Resolve<TfrmConversorView>;
  
  Application.Run;
  GlobalContainer.Free;
end.

2. 视图FormCreate简化

procedure TfrmConversorView.FormCreate(Sender: TObject);
begin
  // 无需手动Resolve,容器已通过属性注入完成FService赋值
end;

内容的提问来源于stack exchange,提问作者Marcelo Borgues

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:15:29