Spring4D在Delphi动态库中依赖注入的异常问题及解决咨询
Spring4D在Delphi动态库中依赖注入的问题解决
问题现象
主程序中注册IOutputHandler接口与实现类TOutputHandler,通过容器解析调用正常;但将主程序的容器传入动态库后,DLL中调用LocalContainer.Resolve<IOutputHandler>()抛出EResolveException,提示无法解析该类型。
原因分析
Delphi中,主程序与动态库作为独立模块,会为同一接口生成独立的TypeInfo实例。Spring4D默认以接口的TypeInfo作为服务注册与解析的标识,因此DLL中的IOutputHandler TypeInfo和主程序的不匹配,容器无法找到对应的注册项。
解决方案
方法1:基于GUID注册与解析服务
利用接口的GUID作为服务标识,绕过TypeInfo的差异问题,这是最直接的解决方案。
步骤:
- 注册服务时,显式指定接口的GUID作为服务键
- 解析服务时,同样通过GUID获取服务
完整代码示例
接口定义(可放在共享单元,主程序和DLL都引用)
type IOutputHandler = interface ['{B6C24A62-971F-4D44-85E5-61D1EFC09469}'] procedure WriteMessage(const AMessage: string); end;
实现类(主程序中)
type TOutputHandler = class(TInterfacedObject, IOutputHandler) procedure WriteMessage(const AMessage: string); end; implementation procedure TOutputHandler.WriteMessage(const AMessage: string); begin WriteLn(AMessage); end;
主程序代码
uses Spring.Container, Spring.Guard; type TInitDIProc = procedure(const AContainer: TContainer); TWriteMessageProc = procedure(const AMessage: string); var container: TContainer; handle: THandle; initDI: TInitDIProc; writeMessage: TWriteMessageProc; begin container := GlobalContainer(); // 用GUID注册服务,替代默认的TypeInfo标识 container.RegisterType<TOutputHandler>.Implements<IOutputHandler, '{B6C24A62-971F-4D44-85E5-61D1EFC09469}'>(); container.Build(); // 主程序内部调用验证 var service := container.Resolve<IOutputHandler>(); service.WriteMessage('Message from main application'); // 加载DLL并传入容器 handle := SafeLoadLibrary('DynamicLibrary.dll'); if handle <> 0 then try initDI := TInitDIProc(GetProcAddress(handle, 'InitDI')); writeMessage := TWriteMessageProc(GetProcAddress(handle, 'WriteMessage')); if Assigned(initDI) and Assigned(writeMessage) then begin initDI(container); writeMessage('Message from dynamic library'); end; finally FreeLibrary(handle); end; end.
动态库代码
uses Spring.Container, Spring.Guard; var LocalContainer: TContainer; procedure InitDI(const AContainer: TContainer); begin Guard.CheckNotNull(AContainer, 'AContainer'); LocalContainer := AContainer; end; procedure WriteMessage(const AMessage: string); var service: IOutputHandler; begin // 通过GUID解析服务,避免TypeInfo差异导致的匹配失败 service := LocalContainer.Resolve<IOutputHandler>('{B6C24A62-971F-4D44-85E5-61D1EFC09469}'); service.WriteMessage(AMessage); end; exports InitDI, WriteMessage; begin end.
方法2:使用共享运行时包(BPL)
将接口定义放在一个运行时包中,主程序和DLL都引用该包。这样接口的TypeInfo会被所有引用包的模块共享,Spring4D的默认注册/解析方式即可正常工作。
注意:主程序和DLL必须使用相同版本的包,且包文件需正确部署到程序运行目录。
内容的提问来源于stack exchange,提问作者Nicko
相关产品推荐
相关产品推荐

