Quick.IOC.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. { ***************************************************************************
  2. Copyright (c) 2016-2020 Kike Pérez
  3. Unit : Quick.IoC
  4. Description : IoC Dependency Injector
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 19/10/2019
  8. Modified : 11/01/2020
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.IoC;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. System.SysUtils,
  26. RTTI,
  27. System.TypInfo,
  28. System.Generics.Collections,
  29. Quick.Logger.Intf,
  30. Quick.Options;
  31. type
  32. TActivatorDelegate<T> = reference to function: T;
  33. TIocRegistration = class
  34. type
  35. TRegisterMode = (rmTransient, rmSingleton, rmScoped);
  36. private
  37. fRegisterMode : TRegisterMode;
  38. fIntfInfo : PTypeInfo;
  39. fImplementation : TClass;
  40. fActivatorDelegate : TActivatorDelegate<TValue>;
  41. public
  42. constructor Create;
  43. property IntfInfo : PTypeInfo read fIntfInfo write fIntfInfo;
  44. property &Implementation : TClass read fImplementation write fImplementation;
  45. function IsSingleton : Boolean;
  46. function IsTransient : Boolean;
  47. function IsScoped : Boolean;
  48. function AsSingleton : TIocRegistration;
  49. function AsTransient : TIocRegistration;
  50. function AsScoped : TIocRegistration;
  51. property ActivatorDelegate : TActivatorDelegate<TValue> read fActivatorDelegate write fActivatorDelegate;
  52. end;
  53. TIocRegistrationInterface = class(TIocRegistration)
  54. private
  55. fInstance : IInterface;
  56. public
  57. property Instance : IInterface read fInstance write fInstance;
  58. end;
  59. TIocRegistrationInstance = class(TIocRegistration)
  60. private
  61. fInstance : TObject;
  62. public
  63. property Instance : TObject read fInstance write fInstance;
  64. end;
  65. TIocRegistration<T> = record
  66. private
  67. fRegistration : TIocRegistration;
  68. public
  69. constructor Create(aRegistration : TIocRegistration);
  70. function AsSingleton : TIocRegistration<T>;
  71. function AsTransient : TIocRegistration<T>;
  72. function AsScoped : TIocRegistration<T>;
  73. function DelegateTo(aDelegate : TActivatorDelegate<T>) : TIocRegistration<T>;
  74. end;
  75. IIocRegistrator = interface
  76. ['{F3B79B15-2874-4B66-9B7F-06E2EBFED1AE}']
  77. function GetKey(aPInfo : PTypeInfo; const aName : string = ''): string;
  78. function RegisterType(aTypeInfo : PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration;
  79. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  80. end;
  81. TIocRegistrator = class(TInterfacedObject,IIocRegistrator)
  82. private
  83. fDependencies : TDictionary<string,TIocRegistration>;
  84. public
  85. constructor Create;
  86. destructor Destroy; override;
  87. property Dependencies : TDictionary<string,TIocRegistration> read fDependencies write fDependencies;
  88. function IsRegistered<TInterface: IInterface; TImplementation: class>(const aName : string = '') : Boolean;
  89. function GetKey(aPInfo : PTypeInfo; const aName : string = ''): string;
  90. function RegisterType<TInterface: IInterface; TImplementation: class>(const aName : string = '') : TIocRegistration<TImplementation>; overload;
  91. function RegisterType(aTypeInfo : PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration; overload;
  92. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration; overload;
  93. function RegisterInstance<T : class>(const aName : string = '') : TIocRegistration<T>; overload;
  94. function RegisterOptions<T : TOptions>(aOptions : T) : TIocRegistration<T>;
  95. end;
  96. IIocContainer = interface
  97. ['{6A486E3C-C5E8-4BE5-8382-7B9BCCFC1BC3}']
  98. function RegisterType(aInterface: PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration;
  99. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  100. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue;
  101. //procedure Build;
  102. end;
  103. IIocInjector = interface
  104. ['{F78E6BBC-2A95-41C9-B231-D05A586B4B49}']
  105. end;
  106. TIocInjector = class(TInterfacedObject,IIocInjector)
  107. end;
  108. IIocResolver = interface
  109. ['{B7C07604-B862-46B2-BF33-FF941BBE53CA}']
  110. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  111. end;
  112. TIocResolver = class(TInterfacedObject,IIocResolver)
  113. private
  114. fRegistrator : TIocRegistrator;
  115. fInjector : TIocInjector;
  116. function CreateInstance(aClass : TClass) : TValue;
  117. public
  118. constructor Create(aRegistrator : TIocRegistrator; aInjector : TIocInjector);
  119. function Resolve<T>(const aName : string = ''): T; overload;
  120. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  121. end;
  122. TIocContainer = class(TInterfacedObject,IIocContainer)
  123. private
  124. fRegistrator : TIocRegistrator;
  125. fResolver : TIocResolver;
  126. fInjector : TIocInjector;
  127. fLogger : ILogger;
  128. function InterfaceTypeInfo(const AGUID : TGUID) : PTypeInfo;
  129. class var
  130. GlobalInstance: TIocContainer;
  131. protected
  132. class constructor Create;
  133. class destructor Destroy;
  134. public
  135. constructor Create;
  136. destructor Destroy; override;
  137. function IsRegistered<TInterface: IInterface; TImplementation: class>(const aName: string): Boolean;
  138. function RegisterType<TInterface: IInterface; TImplementation: class>(const aName : string = '') : TIocRegistration<TImplementation>; overload;
  139. function RegisterType(aInterface: PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration; overload;
  140. function RegisterInstance<T : class>(const aName: string = ''): TIocRegistration<T>; overload;
  141. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration; overload;
  142. function RegisterOptions<T : TOptions>(aOptions : TOptions) : TIocRegistration<T>;
  143. function Resolve<T>(const aName : string = ''): T; overload;
  144. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  145. function AbstractFactory<T : class, constructor>(aClass : TClass) : T;
  146. end;
  147. EIocRegisterError = class(Exception);
  148. EIocResolverError = class(Exception);
  149. //singleton global instance
  150. function GlobalContainer: TIocContainer;
  151. implementation
  152. function GlobalContainer: TIocContainer;
  153. begin
  154. Result := TIocContainer.GlobalInstance;
  155. end;
  156. { TIocRegistration }
  157. constructor TIocRegistration.Create;
  158. begin
  159. fRegisterMode := TRegisterMode.rmTransient;
  160. end;
  161. function TIocRegistration.AsTransient: TIocRegistration;
  162. begin
  163. Result := Self;
  164. fRegisterMode := TRegisterMode.rmTransient;
  165. end;
  166. function TIocRegistration.AsSingleton : TIocRegistration;
  167. begin
  168. Result := Self;
  169. fRegisterMode := TRegisterMode.rmSingleton;
  170. end;
  171. function TIocRegistration.AsScoped: TIocRegistration;
  172. begin
  173. Result := Self;
  174. fRegisterMode := TRegisterMode.rmScoped;
  175. end;
  176. function TIocRegistration.IsTransient: Boolean;
  177. begin
  178. Result := fRegisterMode = TRegisterMode.rmTransient;
  179. end;
  180. function TIocRegistration.IsSingleton: Boolean;
  181. begin
  182. Result := fRegisterMode = TRegisterMode.rmSingleton;
  183. end;
  184. function TIocRegistration.IsScoped: Boolean;
  185. begin
  186. Result := fRegisterMode = TRegisterMode.rmScoped;
  187. end;
  188. { TIocContainer }
  189. class constructor TIocContainer.Create;
  190. begin
  191. GlobalInstance := TIocContainer.Create;
  192. end;
  193. class destructor TIocContainer.Destroy;
  194. begin
  195. if GlobalInstance <> nil then GlobalInstance.Free;
  196. inherited;
  197. end;
  198. function TIocContainer.AbstractFactory<T>(aClass: TClass): T;
  199. begin
  200. Result := fResolver.CreateInstance(aClass).AsType<T>;
  201. end;
  202. constructor TIocContainer.Create;
  203. begin
  204. fLogger := nil;
  205. fRegistrator := TIocRegistrator.Create;
  206. fInjector := TIocInjector.Create;
  207. fResolver := TIocResolver.Create(fRegistrator,fInjector);
  208. end;
  209. destructor TIocContainer.Destroy;
  210. begin
  211. fInjector.Free;
  212. fResolver.Free;
  213. fRegistrator.Free;
  214. inherited;
  215. end;
  216. function TIocContainer.InterfaceTypeInfo(const AGUID : TGUID) : PTypeInfo;
  217. var
  218. ctx : TRttiContext;
  219. rtype : TRttiType;
  220. rtypei : TRttiInterfaceType;
  221. begin
  222. ctx := TRttiContext.Create;
  223. try
  224. for rtype in ctx.GetTypes do
  225. begin
  226. if rtype.TypeKind = TTypeKind.tkInterface then
  227. begin
  228. rtypei := (rtype as TRttiInterfaceType);
  229. if IsEqualGUID(rtypei.GUID,AGUID) then Exit(rtypei.Handle);
  230. end;
  231. end;
  232. finally
  233. ctx.Free;
  234. end;
  235. Result := nil;
  236. end;
  237. function TIocContainer.IsRegistered<TInterface, TImplementation>(const aName: string): Boolean;
  238. begin
  239. Result := fRegistrator.IsRegistered<TInterface,TImplementation>(aName);
  240. end;
  241. function TIocContainer.RegisterType<TInterface, TImplementation>(const aName: string): TIocRegistration<TImplementation>;
  242. begin
  243. Result := fRegistrator.RegisterType<TInterface, TImplementation>(aName);
  244. end;
  245. function TIocContainer.RegisterType(aInterface: PTypeInfo; aImplementation: TClass; const aName: string): TIocRegistration;
  246. begin
  247. Result := fRegistrator.RegisterType(aInterface,aImplementation,aName);
  248. end;
  249. function TIocContainer.RegisterInstance<T>(const aName: string): TIocRegistration<T>;
  250. begin
  251. Result := fRegistrator.RegisterInstance<T>(aName);
  252. end;
  253. function TIocContainer.RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  254. begin
  255. Result := fRegistrator.RegisterInstance(aTypeInfo,aName);
  256. end;
  257. function TIocContainer.RegisterOptions<T>(aOptions: TOptions): TIocRegistration<T>;
  258. begin
  259. Result := fRegistrator.RegisterOptions<T>(aOptions).AsSingleton;
  260. end;
  261. function TIocContainer.Resolve(aServiceType: PTypeInfo; const aName: string): TValue;
  262. begin
  263. Result := fResolver.Resolve(aServiceType,aName);
  264. end;
  265. function TIocContainer.Resolve<T>(const aName : string = ''): T;
  266. begin
  267. Result := fResolver.Resolve<T>(aName);
  268. end;
  269. { TIocRegistrator }
  270. constructor TIocRegistrator.Create;
  271. begin
  272. fDependencies := TDictionary<string,TIocRegistration>.Create;
  273. end;
  274. destructor TIocRegistrator.Destroy;
  275. var
  276. reg : TIocRegistration;
  277. begin
  278. for reg in fDependencies.Values do
  279. begin
  280. if reg <> nil then
  281. begin
  282. //free singleton instances not interfaced
  283. if (reg is TIocRegistrationInstance) and (TIocRegistrationInstance(reg).IsSingleton) then TIocRegistrationInstance(reg).Instance.Free;
  284. reg.Free;
  285. end;
  286. end;
  287. fDependencies.Free;
  288. inherited;
  289. end;
  290. function TIocRegistrator.GetKey(aPInfo : PTypeInfo; const aName : string = ''): string;
  291. begin
  292. {$IFDEF NEXTGEN}
  293. Result := aPInfo.Name.ToString;
  294. {$ELSE}
  295. Result := string(aPInfo.Name);
  296. {$ENDIF}
  297. if not aName.IsEmpty then Result := Result + '.' + aName.ToLower;
  298. end;
  299. function TIocRegistrator.IsRegistered<TInterface, TImplementation>(const aName: string): Boolean;
  300. var
  301. key : string;
  302. reg : TIocRegistration;
  303. begin
  304. Result := False;
  305. key := GetKey(TypeInfo(TInterface),aName);
  306. if fDependencies.TryGetValue(key,reg) then
  307. begin
  308. if reg.&Implementation = TImplementation then Result := True;
  309. end
  310. end;
  311. function TIocRegistrator.RegisterInstance<T>(const aName: string): TIocRegistration<T>;
  312. var
  313. reg : TIocRegistration;
  314. begin
  315. reg := RegisterInstance(TypeInfo(T),aName);
  316. Result := TIocRegistration<T>.Create(reg);
  317. end;
  318. function TIocRegistrator.RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  319. var
  320. key : string;
  321. begin
  322. key := GetKey(aTypeInfo,aName);
  323. if fDependencies.TryGetValue(key,Result) then
  324. begin
  325. if Result.&Implementation = aTypeInfo.TypeData.ClassType then raise EIocRegisterError.Create('Implementation is already registered!');
  326. end
  327. else
  328. begin
  329. Result := TIocRegistrationInstance.Create;
  330. Result.IntfInfo := aTypeInfo;
  331. Result.&Implementation := aTypeInfo.TypeData.ClassType;
  332. //reg.Instance := T.Create;
  333. fDependencies.Add(key,Result);
  334. end;
  335. end;
  336. function TIocRegistrator.RegisterOptions<T>(aOptions: T): TIocRegistration<T>;
  337. var
  338. pInfo : PTypeInfo;
  339. key : string;
  340. reg : TIocRegistration;
  341. begin
  342. pInfo := TypeInfo(IOptions<T>);
  343. key := GetKey(pInfo,'');
  344. if fDependencies.TryGetValue(key,reg) then
  345. begin
  346. if reg.&Implementation = aOptions.ClassType then raise EIocRegisterError.Create('Implementation for this interface is already registered!');
  347. end
  348. else
  349. begin
  350. reg := TIocRegistrationInterface.Create;
  351. reg.IntfInfo := pInfo;
  352. reg.&Implementation := aOptions.ClassType;
  353. TIocRegistrationInterface(reg).Instance := TOptionValue<T>.Create(aOptions);
  354. fDependencies.Add(key,reg);
  355. end;
  356. Result := TIocRegistration<T>.Create(reg);
  357. end;
  358. function TIocRegistrator.RegisterType<TInterface, TImplementation>(const aName: string): TIocRegistration<TImplementation>;
  359. var
  360. reg : TIocRegistration;
  361. begin
  362. reg := RegisterType(TypeInfo(TInterface),TImplementation,aName);
  363. Result := TIocRegistration<TImplementation>.Create(reg);
  364. end;
  365. function TIocRegistrator.RegisterType(aTypeInfo : PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration;
  366. var
  367. key : string;
  368. begin
  369. key := GetKey(aTypeInfo,aName);
  370. if fDependencies.TryGetValue(key,Result) then
  371. begin
  372. if Result.&Implementation = aImplementation then raise EIocRegisterError.Create('Implementation for this interface is already registered!');
  373. end
  374. else
  375. begin
  376. Result := TIocRegistrationInterface.Create;
  377. Result.IntfInfo := aTypeInfo;
  378. Result.&Implementation := aImplementation;
  379. fDependencies.Add(key,Result);
  380. end;
  381. end;
  382. { TIocResolver }
  383. constructor TIocResolver.Create(aRegistrator : TIocRegistrator; aInjector : TIocInjector);
  384. begin
  385. fRegistrator := aRegistrator;
  386. fInjector := aInjector;
  387. end;
  388. function TIocResolver.CreateInstance(aClass: TClass): TValue;
  389. var
  390. ctx : TRttiContext;
  391. rtype : TRttiType;
  392. rmethod : TRttiMethod;
  393. rParam : TRttiParameter;
  394. value : TValue;
  395. values : TArray<TValue>;
  396. begin
  397. Result := nil;
  398. ctx := TRttiContext.Create;
  399. try
  400. rtype := ctx.GetType(aClass);
  401. if rtype = nil then Exit;
  402. for rmethod in TRttiInstanceType(rtype).GetMethods do
  403. begin
  404. if rmethod.IsConstructor then
  405. begin
  406. //if create don't have parameters
  407. if Length(rmethod.GetParameters) = 0 then
  408. begin
  409. Result := rmethod.Invoke(TRttiInstanceType(rtype).MetaclassType,[]);
  410. Break;
  411. end
  412. else
  413. begin
  414. for rParam in rmethod.GetParameters do
  415. begin
  416. value := Resolve(rParam.ParamType.Handle);
  417. values := values + [value];
  418. end;
  419. Result := rmethod.Invoke(TRttiInstanceType(rtype).MetaclassType,values);
  420. Break;
  421. end;
  422. end;
  423. end;
  424. finally
  425. ctx.Free;
  426. end;
  427. end;
  428. function TIocResolver.Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue;
  429. var
  430. key : string;
  431. reg : TIocRegistration;
  432. intf : IInterface;
  433. begin
  434. Result := nil;
  435. reg := nil;
  436. key := fRegistrator.GetKey(aServiceType,aName);
  437. if not fRegistrator.Dependencies.TryGetValue(key,reg) then raise EIocResolverError.CreateFmt('Type "%s" not register for IOC!',[aServiceType.Name]);
  438. //if is singleton return already instance if exists
  439. if reg.IsSingleton then
  440. begin
  441. if reg is TIocRegistrationInterface then
  442. begin
  443. if TIocRegistrationInterface(reg).Instance <> nil then
  444. begin
  445. if TIocRegistrationInterface(reg).Instance.QueryInterface(GetTypeData(aServiceType).Guid,intf) <> 0 then raise EIocResolverError.CreateFmt('Implementation for "%s" not registered!',[aServiceType.Name]);
  446. TValue.Make(@intf,aServiceType,Result);
  447. Exit;
  448. end;
  449. end
  450. else
  451. begin
  452. if TIocRegistrationInstance(reg).Instance <> nil then
  453. begin
  454. Result := TIocRegistrationInstance(reg).Instance;
  455. Exit;
  456. end;
  457. end;
  458. end;
  459. //instance not created yet
  460. if reg.&Implementation = nil then raise EIocResolverError.CreateFmt('Implemention for "%s" not defined!',[aServiceType.Name]);
  461. //use activator if assigned
  462. if reg is TIocRegistrationInterface then
  463. begin
  464. if Assigned(reg.ActivatorDelegate) then TIocRegistrationInterface(reg).Instance := reg.ActivatorDelegate().AsInterface
  465. else TIocRegistrationInterface(reg).Instance := CreateInstance(reg.&Implementation).AsInterface;
  466. if (TIocRegistrationInterface(reg).Instance = nil) or (TIocRegistrationInterface(reg).Instance.QueryInterface(GetTypeData(aServiceType).Guid,intf) <> 0) then raise EIocResolverError.CreateFmt('Implementation for "%s" not registered!',[aServiceType.Name]);
  467. TValue.Make(@intf,aServiceType,Result);
  468. end
  469. else
  470. begin
  471. if Assigned(reg.ActivatorDelegate) then TIocRegistrationInstance(reg).Instance := reg.ActivatorDelegate().AsObject
  472. else
  473. begin
  474. TIocRegistrationInstance(reg).Instance := CreateInstance(reg.&Implementation).AsObject;
  475. end;
  476. Result := TIocRegistrationInstance(reg).Instance;
  477. end;
  478. end;
  479. function TIocResolver.Resolve<T>(const aName : string = ''): T;
  480. var
  481. pInfo : PTypeInfo;
  482. begin
  483. Result := Default(T);
  484. pInfo := TypeInfo(T);
  485. Result := Resolve(pInfo,aName).AsType<T>;
  486. end;
  487. { TIocRegistration<T> }
  488. function TIocRegistration<T>.AsScoped: TIocRegistration<T>;
  489. begin
  490. Result := Self;
  491. fRegistration.AsScoped;
  492. end;
  493. function TIocRegistration<T>.AsSingleton: TIocRegistration<T>;
  494. begin
  495. Result := Self;
  496. fRegistration.AsSingleton;
  497. end;
  498. function TIocRegistration<T>.AsTransient: TIocRegistration<T>;
  499. begin
  500. Result := Self;
  501. fRegistration.AsTransient;
  502. end;
  503. constructor TIocRegistration<T>.Create(aRegistration: TIocRegistration);
  504. begin
  505. fRegistration := aRegistration;
  506. end;
  507. function TIocRegistration<T>.DelegateTo(aDelegate: TActivatorDelegate<T>): TIocRegistration<T>;
  508. begin
  509. Result := Self;
  510. fRegistration.ActivatorDelegate := function: TValue
  511. begin
  512. Result := TValue.From<T>(aDelegate);
  513. end;
  514. end;
  515. end.