TInterfaceResolver&TInjectableObject
TInterfaceResolver&TInjectableObject
{ ************ TInterfaceResolver TInjectableObject 用于控制反转(IoC)/依赖注入(Dependency Injection) }
type
/// 在依赖注入(又名IoC)问题发生时抛出的异常
EInterfaceResolver = class(ESynException);
{$M+}
/// 抽象工厂类,允许以级联方式调用接口解析
// - 你可以从此类继承以链式调用TryResolve()方法,
// 这样TInjectableObject就可以询问多种实现,
// 例如TInterfaceStub、TServiceContainer或TDDDRepositoryRestObjectMapping
// - 这将实现工厂模式,作为一个安全且线程安全的依赖注入/控制反转实现
TInterfaceResolver = class
protected
/// 从此实例解析接口的重写方法
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; virtual; abstract;
public
/// 重写此方法以检查此实例是否实现了aInterface RTTI
// - 此默认实现将在本地IInterface上调用TryResolve(),
// 这有些慢,最好重写此方法
function Implements(aInterface: PRttiInfo): boolean; virtual;
/// 可用于对给定接口执行依赖注入/控制反转
// - 将在其内部解析器列表中搜索提供的接口
// - 如果找到匹配项,则返回TRUE并设置Obj变量
// - 可以这样使用来解析ICalculator接口:
// ! var calc: ICalculator;
// ! begin
// ! if Catalog.Resolve(TypeInfo(ICalculator),calc) then
// ! ... 使用calc方法
function Resolve(aInterface: PRttiInfo; out Obj): boolean; overload;
/// 可用于对给定接口执行依赖注入/控制反转
// - 你应该通过之前调用TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator),...])来注册TGuid接口
// - 如果找到匹配项,则返回TRUE并设置Obj变量
// - 如果aGuid不可用,则返回FALSE(或抛出aRaiseIfNotFound异常)
// - 可以这样使用来解析ICalculator接口:
// ! var calc: ICalculator;
// ! begin
// ! if ServiceContainer.Resolve(ICalculator,cal) then
// ! ... 使用calc方法
function Resolve(const aGuid: TGuid; out Obj;
aRaiseIfNotFound: ESynExceptionClass = nil): boolean; overload;
/// 可用于对给定的一组接口执行多个依赖注入/控制反转
// - 这里的接口和实例作为TypeInfo,@Instance对提供
// - 如果有任何接口无法解析,则抛出EServiceException,除非
// aRaiseExceptionIfNotFound设置为FALSE
procedure ResolveByPair(const aInterfaceObjPairs: array of pointer;
aRaiseExceptionIfNotFound: boolean = true);
/// 可用于对给定的一组接口执行多个依赖注入/控制反转
// - 这里的接口和实例作为TGuid和@Instance提供
// - 你应该通过之前调用TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator),...])来注册TGuid接口
// - 如果有任何接口无法解析,则抛出EServiceException,除非
// aRaiseExceptionIfNotFound设置为FALSE
procedure Resolve(const aInterfaces: array of TGuid;
const aObjs: array of pointer;
aRaiseExceptionIfNotFound: boolean = true); overload;
end;
{$M-}
/// 用于存储TInterfacedObject实例的列表
TInterfacedObjectObjArray = array of TInterfacedObject;
/// 用于存储TInterfaceResolver实例的列表
TInterfaceResolverObjArray = array of TInterfaceResolver;
/// 抽象工厂类,针对单一类型的接口
TInterfaceResolverForSingleInterface = class(TInterfaceResolver)
protected
fInterfaceTypeInfo: PRttiInfo;
fInterfaceAncestors: PRttiInfoDynArray;
fInterfaceAncestorsImplementationEntry: TPointerDynArray;
fImplementationEntry: PInterfaceEntry;
fImplementation: TRttiCustom;
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
function GetImplementationName: RawUtf8;
// 主要的IoC/DI虚拟方法 - 默认情况下调用fImplementation.CreateNew
function CreateInstance: TInterfacedObject; virtual;
public
/// 重写的构造函数,用于检查和存储提供的类以实现接口
constructor Create(aInterface: PRttiInfo;
aImplementation: TInterfacedObjectClass); overload;
/// 重写的构造函数,用于通过TGuid检查和存储提供的类以实现接口
constructor Create(const aInterface: TGuid;
aImplementation: TInterfacedObjectClass); overload;
/// 使用此方法可以将接口解析为新实例
function GetOneInstance(out Obj): boolean;
/// 检查是否可以解析提供的接口RTTI
function Implements(aInterface: PRttiInfo): boolean; override;
published
/// 实现每个存储库实例的类名
property ImplementationClass: RawUtf8
read GetImplementationName;
end;
type
/// TInterfaceResolverList如何存储一个接口/类
TInterfaceResolverListEntry = record
/// 包含TypeInfo(ISomeInterface)
TypeInfo: PRttiInfo;
/// 相关的RTTI - 主要用于调用其ClassNewInstance方法
ImplementationClass: TRttiCustom;
/// 用于快速创建的低级接口VMT信息
InterfaceEntry: PInterfaceEntry;
/// 共享实例
// - 将与TInterfaceResolverListEntries数组一起释放
Instance: IInterface;
end;
PInterfaceResolverListEntry = ^TInterfaceResolverListEntry;
/// TInterfaceResolverList如何存储一个接口/类
TInterfaceResolverListEntries = array of TInterfaceResolverListEntry;
/// TInterfaceResolverList.OnCreateInstance使用的事件签名
TOnResolverCreateInstance = procedure(
Sender: TInterfaceResolver; Instance: TInterfacedObject) of object;
/// 注册一个线程安全的类列表以实现一些接口
// - 如TInterfaceResolverInjected.RegisterGlobal()所用
TInterfaceResolverList = class(TInterfaceResolver)
protected
fEntry: TInterfaceResolverListEntries;
fSafe: TRWLightLock;
fOnCreateInstance: TOnResolverCreateInstance;
function PrepareAddAndWriteLock(aInterface: PRttiInfo;
aImplementationClass: TClass): PInterfaceEntry; // 之后解锁fSafe.WriteUnLock
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
public
/// 检查是否可以从其RTTI解析给定的接口
function Implements(aInterface: PRttiInfo): boolean; override;
/// 为接口注册一个给定的实现类
// - 每次解析都会为aImplementationClass创建一个新实例
procedure Add(aInterface: PRttiInfo;
aImplementationClass: TInterfacedObjectClass); overload;
/// 为接口注册一个给定的实现类实例
// - 每次解析都会返回共享的aImplementation实例
// - aImplementation将由内部注册列表管理
procedure Add(aInterface: PRttiInfo;
aImplementation: TInterfacedObject); overload;
/// 注销接口的给定实现类
// - 如果注册了TInterfacedObject实例,则引发EInterfaceResolver
procedure Delete(aInterface: PRttiInfo);
/// 当创建了新的aImplementationClass实例时调用
property OnCreateInstance: TOnResolverCreateInstance
read fOnCreateInstance write fOnCreateInstance;
/// 低级访问内部注册的接口/类列表
// - 应通过Safe锁定方法进行保护
property Entry: TInterfaceResolverListEntries
read fEntry;
/// 低级访问内部锁以实现线程安全
property Safe: TRWLightLock
read fSafe;
end;
/// 针对任意接口的抽象工厂类
// - 你可以从这个类继承来自定义依赖注入(DI/IoC),
// 通过InjectStub/InjectResolver/InjectInstance方法定义解析,
// 并使用重载的Resolve*()方法来完成实例解析
// - TServiceContainer将继承自此类,作为框架中基于接口的服务的主要入口点(通过TRest.Services)
// - 你可以使用RegisterGlobal()类方法来定义一些进程范围的DI
TInterfaceResolverInjected = class(TInterfaceResolver)
protected
fResolvers: TInterfaceResolverObjArray;
fResolversToBeReleased: TInterfaceResolverObjArray;
fDependencies: TInterfacedObjectObjArray;
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
public
/// 为接口解析定义一个全局类类型
// - 大多数情况下,你需要一个本地的DI/IoC解析列表;但
// 你可以使用此方法注册一组共享和全局的解析模式,这些模式在整个注入过程中是通用的
// - 默认情况下,此单元将注册TAutoLocker和TLockedDocVariant以实现IAutoLocker和ILockedDocVariant接口
class procedure RegisterGlobal(aInterface: PRttiInfo;
aImplementationClass: TInterfacedObjectClass); overload;
/// 为接口解析定义一个全局实例
// - 大多数情况下,你需要一个本地的DI/IoC解析列表;但
// 你可以使用此方法注册一组共享和全局的解析模式,这些模式在整个注入过程中是通用的
// - 提供的实例将由全局列表拥有(增加其内部引用计数),
// 直到通过RegisterGlobalDelete()释放
// - 如果之前未通过RegisterGlobalDelete()释放,则在单元的最终化过程中释放提供的实例
class procedure RegisterGlobal(aInterface: PRttiInfo;
aImplementation: TInterfacedObject); overload;
/// 取消定义一个全局接口解析实例
// - 你可以注销之前通过RegisterGlobal(aInterface,aImplementation)定义的给定实例
// - 如果你没有调用RegisterGlobalDelete(),则剩余实例将在单元的最终化过程中被释放
class procedure RegisterGlobalDelete(aInterface: PRttiInfo);
/// 准备并设置接口DI/IoC解析,使用其TGuid指定的空TInterfaceStub
procedure InjectStub(const aStubsByGuid: array of TGuid); overload; virtual;
/// 准备并设置接口DI/IoC解析,使用TInterfaceResolver类型的工厂
// - 例如,自定义的TInterfaceStub/TInterfaceMock,TServiceContainer,
// TDDDRepositoryRestObjectMapping或任何工厂类
// - 默认情况下,只有TInterfaceStub/TInterfaceMock将由此实例拥有,并在Destroy时释放
// - 除非设置了OwnOtherResolvers,否则其他解析器不会被释放
procedure InjectResolver(const aOtherResolvers: array of TInterfaceResolver;
OwnOtherResolvers: boolean = false); overload; virtual;
/// 从TInterfacedObject实例准备并设置接口DI/IoC解析
// - 声明为依赖项的任何TInterfacedObject的引用计数将增加,并在Destroy时减少
procedure InjectInstance(const aDependencies: array of TInterfacedObject);
overload; virtual;
/// 检查给定的接口是否可以通过其RTTI解析
function Implements(aInterface: PRttiInfo): boolean; override;
/// 删除之前注册的解析器
// - 之后可以重新注册aResolver
procedure DeleteResolver(aResolver: TInterfaceResolver);
/// 释放所有已使用的实例
// - 包括Inject(aStubsByGuid)中指定的所有TInterfaceStub实例
// - 将在所有TInterfacedObject依赖项上调用_Release
destructor Destroy; override;
end;
/// 任何服务实现类都可以继承自此类,以允许框架进行依赖注入(也称为SOLID DI/IoC)
// - 创建后,框架将调用AddResolver()成员,以便其Resolve*()方法可用于注入任何所需的依赖项以进行延迟依赖项解析(例如,在公共属性getter中)
// - 发布的任何接口属性也将自动注入
// - 如果你使用此类实现了SOA服务,则TRestServer.Services将通过TServiceFactoryServer.CreateInstance()自动注入
TInjectableObject = class(TInterfacedObjectWithCustomCreate)
protected
fResolver: TInterfaceResolver;
fResolverOwned: boolean;
fRtti: TRttiCustom;
// DI/IoC解析受保护的方法
function TryResolve(aInterface: PRttiInfo; out Obj): boolean;
/// 此方法将解析所有接口发布的属性
procedure AutoResolve(aRaiseEServiceExceptionIfNotFound: boolean);
public
/// 初始化实例,定义一个或多个依赖项解析方式
// - 可以直接从其TGuid创建简单的TInterfaceStub,
// 然后可以指定任何类型的DI/IoC解析器实例,即
// 自定义的TInterfaceStub/TInterfaceMock,TServiceContainer或
// TDDDRepositoryRestObjectMapping,然后在依赖项解析期间将使用任何TInterfacedObject实例:
// ! procedure TMyTestCase.OneTestCaseMethod;
// ! var Test: IServiceToBeTested;
// ! begin
// ! Test := TServiceToBeTested.CreateInjected(
// ! [ICalculator],
// ! [TInterfaceMock.Create(IPersistence,self).
// ! ExpectsCount('SaveItem',qoEqualTo,1),
// ! RestInstance.Services],
// ! [AnyInterfacedObject]);
// ! ...
// - 请注意,所有注入的存根/模拟实例都将由TInjectableObject拥有,并在释放时一起释放
// - 声明为依赖项的任何TInterfacedObject的引用计数将增加,并在Destroy时减少
// - 定义DI/IoC后,将调用受保护的AutoResolve()方法
constructor CreateInjected(const aStubsByGuid: array of TGuid;
const aOtherResolvers: array of TInterfaceResolver;
const aDependencies: array of TInterfacedObject;
aRaiseEServiceExceptionIfNotFound: boolean = true); virtual;
/// 初始化实例,定义一个依赖项解析器
// - 解析器可能是例如TServiceContainer
// - 定义DI/IoC后,将调用受保护的AutoResolve()方法
// - 如TServiceFactoryServer.CreateInstance所调用
constructor CreateWithResolver(aResolver: TInterfaceResolver;
aRaiseEServiceExceptionIfNotFound: boolean = true); virtual;
/// 可用于对给定接口类型信息进行DI/IoC
procedure Resolve(aInterface: PRttiInfo; out Obj); overload;
/// 可用于对给定接口TGuid进行DI/IoC
procedure Resolve(const aGuid: TGuid; out Obj); overload;
/// 可用于对给定的一组接口执行多个DI/IoC
// - 此处接口和实例作为TypeInfo,@Instance对提供
procedure ResolveByPair(const aInterfaceObjPairs: array of pointer);
/// 可用于对给定的一组接口执行多个DI/IoC
// - 此处接口和实例作为TGuid和指针提供
procedure Resolve(const aInterfaces: array of TGuid;
const aObjs: array of pointer); overload;
/// 释放所有已使用的实例
// - 包括CreateInjected()中指定的所有TInterfaceStub实例
destructor Destroy; override;
/// 访问关联的依赖项解析器(如果有)
property Resolver: TInterfaceResolver
read fResolver;
end;
/// TInjectableObject类型的类引用类型(元类)
TInjectableObjectClass = class of TInjectableObject;
var
/// 全局线程安全列表,用于进程范围内的接口解析
// - TInterfaceResolverInjected.RegisterGlobal/RegisterGlobalDelete
// 类方法重定向到GlobalInterfaceResolver.Add/Delete
// - 本单元的初始化部分将在启动时执行:
// ! GlobalInterfaceResolver.Add(TypeInfo(IAutoLocker), TAutoLocker);
// ! GlobalInterfaceResolver.Add(TypeInfo(ILockedDocVariant), TLockedDocVariant);
GlobalInterfaceResolver: TInterfaceResolverList;