Quick.IOC.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 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 : 12/11/2019
  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 GetKey(aPInfo : PTypeInfo; const aName : string = ''): string;
  89. function RegisterType<TInterface: IInterface; TImplementation: class>(const aName : string = '') : TIocRegistration<TImplementation>; overload;
  90. function RegisterType(aTypeInfo : PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration; overload;
  91. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration; overload;
  92. function RegisterInstance<T : class>(const aName : string = '') : TIocRegistration<T>; overload;
  93. function RegisterOptions<T : TOptions>(aOptions : T) : TIocRegistration<T>;
  94. end;
  95. IIocContainer = interface
  96. ['{6A486E3C-C5E8-4BE5-8382-7B9BCCFC1BC3}']
  97. function RegisterType(aInterface: PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration;
  98. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  99. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue;
  100. //procedure Build;
  101. end;
  102. IIocInjector = interface
  103. ['{F78E6BBC-2A95-41C9-B231-D05A586B4B49}']
  104. end;
  105. TIocInjector = class(TInterfacedObject,IIocInjector)
  106. end;
  107. IIocResolver = interface
  108. ['{B7C07604-B862-46B2-BF33-FF941BBE53CA}']
  109. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  110. end;
  111. TIocResolver = class(TInterfacedObject,IIocResolver)
  112. private
  113. fRegistrator : TIocRegistrator;
  114. fInjector : TIocInjector;
  115. function CreateInstance(aClass : TClass) : TValue;
  116. public
  117. constructor Create(aRegistrator : TIocRegistrator; aInjector : TIocInjector);
  118. function Resolve<T>(const aName : string = ''): T; overload;
  119. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  120. end;
  121. TIocContainer = class(TInterfacedObject,IIocContainer)
  122. private
  123. fRegistrator : TIocRegistrator;
  124. fResolver : TIocResolver;
  125. fInjector : TIocInjector;
  126. fLogger : ILogger;
  127. function InterfaceTypeInfo(const AGUID : TGUID) : PTypeInfo;
  128. class var
  129. GlobalInstance: TIocContainer;
  130. protected
  131. class constructor Create;
  132. class destructor Destroy;
  133. public
  134. constructor Create;
  135. destructor Destroy; override;
  136. function RegisterType<TInterface: IInterface; TImplementation: class>(const aName : string = '') : TIocRegistration<TImplementation>; overload;
  137. function RegisterType(aInterface: PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration; overload;
  138. function RegisterInstance<T : class>(const aName: string = ''): TIocRegistration<T>; overload;
  139. function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration; overload;
  140. function RegisterOptions<T : TOptions>(aOptions : TOptions) : TIocRegistration<T>;
  141. function Resolve<T>(const aName : string = ''): T; overload;
  142. function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
  143. function AbstractFactory<T : class, constructor>(aClass : TClass) : T;
  144. end;
  145. EIocRegisterError = class(Exception);
  146. EIocResolverError = class(Exception);
  147. //singleton global instance
  148. function GlobalContainer: TIocContainer;
  149. implementation
  150. function GlobalContainer: TIocContainer;
  151. begin
  152. Result := TIocContainer.GlobalInstance;
  153. end;
  154. { TIocRegistration }
  155. constructor TIocRegistration.Create;
  156. begin
  157. fRegisterMode := TRegisterMode.rmTransient;
  158. end;
  159. function TIocRegistration.AsTransient: TIocRegistration;
  160. begin
  161. Result := Self;
  162. fRegisterMode := TRegisterMode.rmTransient;
  163. end;
  164. function TIocRegistration.AsSingleton : TIocRegistration;
  165. begin
  166. Result := Self;
  167. fRegisterMode := TRegisterMode.rmSingleton;
  168. end;
  169. function TIocRegistration.AsScoped: TIocRegistration;
  170. begin
  171. Result := Self;
  172. fRegisterMode := TRegisterMode.rmScoped;
  173. end;
  174. function TIocRegistration.IsTransient: Boolean;
  175. begin
  176. Result := fRegisterMode = TRegisterMode.rmTransient;
  177. end;
  178. function TIocRegistration.IsSingleton: Boolean;
  179. begin
  180. Result := fRegisterMode = TRegisterMode.rmSingleton;
  181. end;
  182. function TIocRegistration.IsScoped: Boolean;
  183. begin
  184. Result := fRegisterMode = TRegisterMode.rmScoped;
  185. end;
  186. { TIocContainer }
  187. class constructor TIocContainer.Create;
  188. begin
  189. GlobalInstance := TIocContainer.Create;
  190. end;
  191. class destructor TIocContainer.Destroy;
  192. begin
  193. if GlobalInstance <> nil then GlobalInstance.Free;
  194. inherited;
  195. end;
  196. function TIocContainer.AbstractFactory<T>(aClass: TClass): T;
  197. begin
  198. Result := fResolver.CreateInstance(aClass).AsType<T>;
  199. end;
  200. constructor TIocContainer.Create;
  201. begin
  202. fLogger := nil;
  203. fRegistrator := TIocRegistrator.Create;
  204. fInjector := TIocInjector.Create;
  205. fResolver := TIocResolver.Create(fRegistrator,fInjector);
  206. end;
  207. destructor TIocContainer.Destroy;
  208. begin
  209. fInjector.Free;
  210. fResolver.Free;
  211. fRegistrator.Free;
  212. inherited;
  213. end;
  214. function TIocContainer.InterfaceTypeInfo(const AGUID : TGUID) : PTypeInfo;
  215. var
  216. ctx : TRttiContext;
  217. rtype : TRttiType;
  218. rtypei : TRttiInterfaceType;
  219. begin
  220. ctx := TRttiContext.Create;
  221. try
  222. for rtype in ctx.GetTypes do
  223. begin
  224. if rtype.TypeKind = TTypeKind.tkInterface then
  225. begin
  226. rtypei := (rtype as TRttiInterfaceType);
  227. if IsEqualGUID(rtypei.GUID,AGUID) then Exit(rtypei.Handle);
  228. end;
  229. end;
  230. finally
  231. ctx.Free;
  232. end;
  233. Result := nil;
  234. end;
  235. function TIocContainer.RegisterType<TInterface, TImplementation>(const aName: string): TIocRegistration<TImplementation>;
  236. begin
  237. Result := fRegistrator.RegisterType<TInterface, TImplementation>(aName);
  238. end;
  239. function TIocContainer.RegisterType(aInterface: PTypeInfo; aImplementation: TClass; const aName: string): TIocRegistration;
  240. begin
  241. Result := fRegistrator.RegisterType(aInterface,aImplementation,aName);
  242. end;
  243. function TIocContainer.RegisterInstance<T>(const aName: string): TIocRegistration<T>;
  244. begin
  245. Result := fRegistrator.RegisterInstance<T>(aName);
  246. end;
  247. function TIocContainer.RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  248. begin
  249. Result := fRegistrator.RegisterInstance(aTypeInfo,aName);
  250. end;
  251. function TIocContainer.RegisterOptions<T>(aOptions: TOptions): TIocRegistration<T>;
  252. begin
  253. Result := fRegistrator.RegisterOptions<T>(aOptions).AsSingleton;
  254. end;
  255. function TIocContainer.Resolve(aServiceType: PTypeInfo; const aName: string): TValue;
  256. begin
  257. Result := fResolver.Resolve(aServiceType,aName);
  258. end;
  259. function TIocContainer.Resolve<T>(const aName : string = ''): T;
  260. begin
  261. Result := fResolver.Resolve<T>(aName);
  262. end;
  263. { TIocRegistrator }
  264. constructor TIocRegistrator.Create;
  265. begin
  266. fDependencies := TDictionary<string,TIocRegistration>.Create;
  267. end;
  268. destructor TIocRegistrator.Destroy;
  269. var
  270. reg : TIocRegistration;
  271. begin
  272. for reg in fDependencies.Values do
  273. begin
  274. if reg <> nil then
  275. begin
  276. //free singleton instances not interfaced
  277. if (reg is TIocRegistrationInstance) and (TIocRegistrationInstance(reg).IsSingleton) then TIocRegistrationInstance(reg).Instance.Free;
  278. reg.Free;
  279. end;
  280. end;
  281. fDependencies.Free;
  282. inherited;
  283. end;
  284. function TIocRegistrator.GetKey(aPInfo : PTypeInfo; const aName : string = ''): string;
  285. begin
  286. {$IFDEF NEXTGEN}
  287. Result := aPInfo.Name.ToString;
  288. {$ELSE}
  289. Result := string(aPInfo.Name);
  290. {$ENDIF}
  291. if not aName.IsEmpty then Result := Result + '.' + aName.ToLower;
  292. end;
  293. function TIocRegistrator.RegisterInstance<T>(const aName: string): TIocRegistration<T>;
  294. var
  295. reg : TIocRegistration;
  296. begin
  297. reg := RegisterInstance(TypeInfo(T),aName);
  298. Result := TIocRegistration<T>.Create(reg);
  299. end;
  300. function TIocRegistrator.RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration;
  301. var
  302. key : string;
  303. begin
  304. key := GetKey(aTypeInfo,aName);
  305. if fDependencies.TryGetValue(key,Result) then
  306. begin
  307. if Result.&Implementation = aTypeInfo.TypeData.ClassType then raise EIocRegisterError.Create('Implementation is already registered!');
  308. end
  309. else
  310. begin
  311. Result := TIocRegistrationInstance.Create;
  312. Result.IntfInfo := aTypeInfo;
  313. Result.&Implementation := aTypeInfo.TypeData.ClassType;
  314. //reg.Instance := T.Create;
  315. fDependencies.Add(key,Result);
  316. end;
  317. end;
  318. function TIocRegistrator.RegisterOptions<T>(aOptions: T): TIocRegistration<T>;
  319. var
  320. pInfo : PTypeInfo;
  321. key : string;
  322. reg : TIocRegistration;
  323. begin
  324. pInfo := TypeInfo(IOptions<T>);
  325. key := GetKey(pInfo,'');
  326. if fDependencies.TryGetValue(key,reg) then
  327. begin
  328. if reg.&Implementation = aOptions.ClassType then raise EIocRegisterError.Create('Implementation for this interface is already registered!');
  329. end
  330. else
  331. begin
  332. reg := TIocRegistrationInterface.Create;
  333. reg.IntfInfo := pInfo;
  334. reg.&Implementation := aOptions.ClassType;
  335. TIocRegistrationInterface(reg).Instance := TOptionValue<T>.Create(aOptions);
  336. fDependencies.Add(key,reg);
  337. end;
  338. Result := TIocRegistration<T>.Create(reg);
  339. end;
  340. function TIocRegistrator.RegisterType<TInterface, TImplementation>(const aName: string): TIocRegistration<TImplementation>;
  341. var
  342. reg : TIocRegistration;
  343. begin
  344. reg := RegisterType(TypeInfo(TInterface),TImplementation,aName);
  345. Result := TIocRegistration<TImplementation>.Create(reg);
  346. end;
  347. function TIocRegistrator.RegisterType(aTypeInfo : PTypeInfo; aImplementation : TClass; const aName : string = '') : TIocRegistration;
  348. var
  349. key : string;
  350. begin
  351. key := GetKey(aTypeInfo,aName);
  352. if fDependencies.TryGetValue(key,Result) then
  353. begin
  354. if Result.&Implementation = aImplementation then raise EIocRegisterError.Create('Implementation for this interface is already registered!');
  355. end
  356. else
  357. begin
  358. Result := TIocRegistrationInterface.Create;
  359. Result.IntfInfo := aTypeInfo;
  360. Result.&Implementation := aImplementation;
  361. fDependencies.Add(key,Result);
  362. end;
  363. end;
  364. { TIocResolver }
  365. constructor TIocResolver.Create(aRegistrator : TIocRegistrator; aInjector : TIocInjector);
  366. begin
  367. fRegistrator := aRegistrator;
  368. fInjector := aInjector;
  369. end;
  370. function TIocResolver.CreateInstance(aClass: TClass): TValue;
  371. var
  372. ctx : TRttiContext;
  373. rtype : TRttiType;
  374. rmethod : TRttiMethod;
  375. rParam : TRttiParameter;
  376. value : TValue;
  377. values : TArray<TValue>;
  378. begin
  379. Result := nil;
  380. ctx := TRttiContext.Create;
  381. try
  382. rtype := ctx.GetType(aClass);
  383. if rtype = nil then Exit;
  384. for rmethod in TRttiInstanceType(rtype).GetMethods do
  385. begin
  386. if rmethod.IsConstructor then
  387. begin
  388. //if create don't have parameters
  389. if Length(rmethod.GetParameters) = 0 then
  390. begin
  391. Result := rmethod.Invoke(TRttiInstanceType(rtype).MetaclassType,[]);
  392. Break;
  393. end
  394. else
  395. begin
  396. for rParam in rmethod.GetParameters do
  397. begin
  398. value := Resolve(rParam.ParamType.Handle);
  399. values := values + [value];
  400. end;
  401. Result := rmethod.Invoke(TRttiInstanceType(rtype).MetaclassType,values);
  402. Break;
  403. end;
  404. end;
  405. end;
  406. finally
  407. ctx.Free;
  408. end;
  409. end;
  410. function TIocResolver.Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue;
  411. var
  412. key : string;
  413. reg : TIocRegistration;
  414. intf : IInterface;
  415. begin
  416. Result := nil;
  417. reg := nil;
  418. key := fRegistrator.GetKey(aServiceType,aName);
  419. if not fRegistrator.Dependencies.TryGetValue(key,reg) then raise EIocResolverError.CreateFmt('Type "%s" not register for IOC!',[aServiceType.Name]);
  420. //if is singleton return already instance if exists
  421. if reg.IsSingleton then
  422. begin
  423. if reg is TIocRegistrationInterface then
  424. begin
  425. if TIocRegistrationInterface(reg).Instance <> nil then
  426. begin
  427. if TIocRegistrationInterface(reg).Instance.QueryInterface(GetTypeData(aServiceType).Guid,intf) <> 0 then raise EIocResolverError.CreateFmt('Implementation for "%s" not registered!',[aServiceType.Name]);
  428. TValue.Make(@intf,aServiceType,Result);
  429. Exit;
  430. end;
  431. end
  432. else
  433. begin
  434. if TIocRegistrationInstance(reg).Instance <> nil then
  435. begin
  436. Result := TIocRegistrationInstance(reg).Instance;
  437. Exit;
  438. end;
  439. end;
  440. end;
  441. //instance not created yet
  442. if reg.&Implementation = nil then raise EIocResolverError.CreateFmt('Implemention for "%s" not defined!',[aServiceType.Name]);
  443. //use activator if assigned
  444. if reg is TIocRegistrationInterface then
  445. begin
  446. if Assigned(reg.ActivatorDelegate) then TIocRegistrationInterface(reg).Instance := reg.ActivatorDelegate().AsInterface
  447. else TIocRegistrationInterface(reg).Instance := CreateInstance(reg.&Implementation).AsInterface;
  448. 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]);
  449. TValue.Make(@intf,aServiceType,Result);
  450. end
  451. else
  452. begin
  453. if Assigned(reg.ActivatorDelegate) then TIocRegistrationInstance(reg).Instance := reg.ActivatorDelegate().AsObject
  454. else
  455. begin
  456. TIocRegistrationInstance(reg).Instance := CreateInstance(reg.&Implementation).AsObject;
  457. end;
  458. Result := TIocRegistrationInstance(reg).Instance;
  459. end;
  460. end;
  461. function TIocResolver.Resolve<T>(const aName : string = ''): T;
  462. var
  463. pInfo : PTypeInfo;
  464. begin
  465. Result := Default(T);
  466. pInfo := TypeInfo(T);
  467. Result := Resolve(pInfo,aName).AsType<T>;
  468. end;
  469. { TIocRegistration<T> }
  470. function TIocRegistration<T>.AsScoped: TIocRegistration<T>;
  471. begin
  472. Result := Self;
  473. fRegistration.AsScoped;
  474. end;
  475. function TIocRegistration<T>.AsSingleton: TIocRegistration<T>;
  476. begin
  477. Result := Self;
  478. fRegistration.AsSingleton;
  479. end;
  480. function TIocRegistration<T>.AsTransient: TIocRegistration<T>;
  481. begin
  482. Result := Self;
  483. fRegistration.AsTransient;
  484. end;
  485. constructor TIocRegistration<T>.Create(aRegistration: TIocRegistration);
  486. begin
  487. fRegistration := aRegistration;
  488. end;
  489. function TIocRegistration<T>.DelegateTo(aDelegate: TActivatorDelegate<T>): TIocRegistration<T>;
  490. begin
  491. Result := Self;
  492. fRegistration.ActivatorDelegate := function: TValue
  493. begin
  494. Result := TValue.From<T>(aDelegate);
  495. end;
  496. end;
  497. end.