container-ioc
Advanced tools
Comparing version
@@ -0,1 +1,11 @@ | ||
<a name="1.7.3"></a> | ||
# [1.7.3]() (2017-10-07) | ||
### API changes | ||
#### Container | ||
* **Added option parameter to constructor**. - options object has two attributes: (parent and defaultLifeTime). | ||
### Fixed | ||
* **Factories always resolve instances**. | ||
<a name="1.7.0"></a> | ||
@@ -2,0 +12,0 @@ # [1.7.0]() (2017-10-07) |
import { IInjectionInstance, ProviderToken, RegistrationProvider } from './interfaces'; | ||
import { IContainer } from './container.interface'; | ||
import { IContainer, IContainerOptions } from './container.interface'; | ||
export declare class Container implements IContainer { | ||
private static DEFAULT_LIFE_TIME; | ||
private registry; | ||
private parent; | ||
private registry; | ||
constructor(parent?: IContainer | undefined); | ||
private defaultLifeTime; | ||
constructor(options?: IContainerOptions); | ||
register(provider: RegistrationProvider | RegistrationProvider[]): void; | ||
resolve(token: ProviderToken): IInjectionInstance; | ||
createScope(): IContainer; | ||
createChild(): IContainer; | ||
setParent(parent: IContainer): void; | ||
createChild(): IContainer; | ||
private resolveInternal(token, traceMessage?); | ||
private registerAll(providers); | ||
private registerOne(provider); | ||
private createInstance(cls, message); | ||
private convertTokensToInjectionMd(tokens); | ||
private instantiateWithFactory(factory, traceMessage); | ||
private nornalizeProvider(provider); | ||
@@ -22,3 +25,3 @@ private normalizeSingleProvider(provider); | ||
private isInjectable(cls); | ||
private getInjections(cls); | ||
private retrieveInjectionsFromClass(cls); | ||
} |
@@ -1,2 +0,6 @@ | ||
import { IInjectionInstance, ProviderToken, RegistrationProvider } from './interfaces'; | ||
import { IInjectionInstance, ProviderToken, RegistrationProvider, LifeTime } from './interfaces'; | ||
export interface IContainerOptions { | ||
parent?: IContainer; | ||
defaultLifeTime?: LifeTime; | ||
} | ||
export interface IContainer { | ||
@@ -3,0 +7,0 @@ register(provider: RegistrationProvider | RegistrationProvider[]): void; |
@@ -10,5 +10,9 @@ "use strict"; | ||
class Container { | ||
constructor(parent) { | ||
this.parent = parent; | ||
constructor(options) { | ||
this.registry = new Map(); | ||
this.defaultLifeTime = Container.DEFAULT_LIFE_TIME; | ||
if (options) { | ||
this.parent = options.parent; | ||
this.defaultLifeTime = options.defaultLifeTime || this.defaultLifeTime; | ||
} | ||
} | ||
@@ -29,10 +33,10 @@ register(provider) { | ||
createScope() { | ||
return new Container(this); | ||
return new Container({ parent: this }); | ||
} | ||
createChild() { | ||
return this.createScope(); | ||
} | ||
setParent(parent) { | ||
this.parent = parent; | ||
} | ||
createChild() { | ||
return new Container(this); | ||
} | ||
resolveInternal(token, traceMessage) { | ||
@@ -50,15 +54,3 @@ traceMessage = this.buildTraceMessage(token, traceMessage); | ||
} | ||
if (registryData.factory) { | ||
let injections = []; | ||
if (registryData.injections) { | ||
injections = registryData.injections.map(i => this.resolveInternal(i, traceMessage)); | ||
} | ||
return registryData.factory(...injections); | ||
} | ||
const constructor = registryData.cls; | ||
const isInjectable = this.isInjectable(constructor); | ||
if (!isInjectable) { | ||
throw new exceptions_1.ClassNotInjectableError(constructor.name); | ||
} | ||
const instance = this.createInstance(constructor, traceMessage); | ||
const instance = this.instantiateWithFactory(registryData.factory, traceMessage); | ||
if (registryData.lifeTime === interfaces_1.LifeTime.Persistent) { | ||
@@ -78,20 +70,44 @@ registryData.instance = instance; | ||
} | ||
else if (provider.useClass) { | ||
registryData.cls = provider.useClass; | ||
else { | ||
const factoryValue = provider.useFactory || provider.useClass; | ||
const isClass = this.isInjectable(factoryValue); | ||
registryData.factory = { | ||
value: factoryValue, | ||
isClass | ||
}; | ||
if (isClass) { | ||
registryData.factory.inject = this.retrieveInjectionsFromClass(registryData.factory.value); | ||
} | ||
else { | ||
registryData.factory.inject = this.convertTokensToInjectionMd(provider.inject); | ||
} | ||
registryData.lifeTime = provider.lifeTime || this.defaultLifeTime; | ||
} | ||
else if (provider.useFactory) { | ||
registryData.factory = provider.useFactory; | ||
registryData.injections = provider.inject; | ||
} | ||
registryData.lifeTime = provider.lifeTime || interfaces_1.LifeTime.Persistent; | ||
this.registry.set(provider.token, registryData); | ||
} | ||
createInstance(cls, message) { | ||
const injectionsMd = this.getInjections(cls); | ||
const resolvedInjections = injectionsMd.map(injectionMd => this.resolveInternal(injectionMd.token, message)); | ||
convertTokensToInjectionMd(tokens) { | ||
let injections = []; | ||
if (tokens) { | ||
injections = tokens.map((token, index) => { | ||
return { | ||
token, | ||
parameterIndex: index | ||
}; | ||
}); | ||
} | ||
return injections; | ||
} | ||
instantiateWithFactory(factory, traceMessage) { | ||
const injections = factory.inject; | ||
const resolvedInjections = injections.map(injection => this.resolveInternal(injection.token, traceMessage)); | ||
const args = []; | ||
injectionsMd.forEach((injection, index) => { | ||
injections.forEach((injection, index) => { | ||
args[injection.parameterIndex] = resolvedInjections[index]; | ||
}); | ||
return new cls(...args); | ||
if (factory.isClass) { | ||
return new factory.value(...args); | ||
} | ||
else { | ||
return factory.value(...args); | ||
} | ||
} | ||
@@ -145,6 +161,8 @@ nornalizeProvider(provider) { | ||
} | ||
getInjections(cls) { | ||
return MetadataAnnotator.getMetadata(keys_1.INJECTIONS_MD_KEY, cls) || []; | ||
retrieveInjectionsFromClass(cls) { | ||
const injections = MetadataAnnotator.getMetadata(keys_1.INJECTIONS_MD_KEY, cls) || []; | ||
return this.convertTokensToInjectionMd(injections); | ||
} | ||
} | ||
Container.DEFAULT_LIFE_TIME = interfaces_1.LifeTime.Persistent; | ||
exports.Container = Container; |
@@ -11,8 +11,3 @@ "use strict"; | ||
const injectionMd = MetadataAnnotator.getMetadata(keys_1.INJECTIONS_MD_KEY, target) || []; | ||
injections.forEach((injectionToken, injectionIndex) => { | ||
injectionMd.push({ | ||
token: injectionToken, | ||
parameterIndex: injectionIndex | ||
}); | ||
}); | ||
injections.forEach(token => injectionMd.push(token)); | ||
MetadataAnnotator.defineMetadata(keys_1.INJECTIONS_MD_KEY, injectionMd, target); | ||
@@ -26,6 +21,3 @@ } | ||
const injections = MetadataAnnotator.getMetadata(keys_1.INJECTIONS_MD_KEY, target) || []; | ||
injections.push({ | ||
token, | ||
parameterIndex | ||
}); | ||
injections.push(token); | ||
MetadataAnnotator.defineMetadata(keys_1.INJECTIONS_MD_KEY, injections, target); | ||
@@ -32,0 +24,0 @@ }; |
@@ -1,7 +0,11 @@ | ||
import { IConstructor, IInjectionInstance, LifeTime, ProviderToken } from './interfaces'; | ||
import { IConstructor, IInjectionInstance, IInjectionMd, LifeTime } from './interfaces'; | ||
export declare type FactoryFunction = (...args: any[]) => any; | ||
export interface IFactory { | ||
value: IConstructor | FactoryFunction; | ||
isClass: boolean; | ||
inject?: IInjectionMd[]; | ||
} | ||
export interface IRegistryData { | ||
instance: IInjectionInstance; | ||
cls: IConstructor; | ||
factory: (...args: any[]) => any; | ||
injections: ProviderToken[]; | ||
factory: IFactory; | ||
lifeTime: LifeTime; | ||
@@ -11,6 +15,4 @@ } | ||
instance: IInjectionInstance; | ||
cls: IConstructor; | ||
factory: (...args: any[]) => any; | ||
injections: ProviderToken[]; | ||
factory: IFactory; | ||
lifeTime: LifeTime; | ||
} |
{ | ||
"name": "container-ioc", | ||
"version": "1.7.1", | ||
"version": "1.7.3", | ||
"description": "Dependency Injection and Inversion of Control (IoC) container", | ||
@@ -5,0 +5,0 @@ "author": "Alexander Kozlov", |
@@ -107,3 +107,4 @@  | ||
### Life Time control. | ||
> By default, containers resolve singletons when registering with **useClass**. Change it by setting **lifeTime** attribute to **LifeTime.PerRequest**. | ||
> By default, containers resolve singletons when using **useClass** and **useFactory**. | ||
Default life time for all items in a container can be set by passing an option object to it's contructor with **defailtLifeTime** attribute. Possible values: **LifeTime.PerRequest** (resolves instances) and **LifeTime.Persistent** (resolves singletons); | ||
@@ -113,22 +114,44 @@ ```typescript | ||
const container = new Container({ | ||
defaultLifeTime: LifeTime.PerRequest | ||
}); | ||
``` | ||
> You can also specify life time individually for each item in a container by specifying **lifeTime** attribute. | ||
```typescript | ||
container.register([ | ||
{ token: TService, useClass: Service, lifeTime: LifeTime.PerRequest } | ||
{ | ||
token: TService, | ||
useClass: Service, | ||
lifeTime: LifeTime.PerRequest | ||
} | ||
]); | ||
``` | ||
```typescript | ||
container.register([ | ||
{ | ||
token: TService, | ||
useFactory: () => { | ||
return { | ||
serve(): void {} | ||
} | ||
}, | ||
lifeTime: LifeTime.Persistent | ||
} | ||
]); | ||
``` | ||
### Hierarchical containers. | ||
> If container can't find value, it will look it up in ascendant containers. | ||
> If a container can't find a value within itself, it will look it up in ascendant containers. There a 3 ways to set a parent for a container. | ||
###### 1. Container.createChild() method. | ||
```typescript | ||
const parentContainer = new Container(); | ||
const childContainer = parentContainer.createChild(); | ||
``` | ||
let parentContainer = new Container(); | ||
let childContainer = parentContainer.createChild(); | ||
parentContainer.register({ token: TApplication, useClass: Application }); | ||
childContainer.resolve(TApplication); | ||
``` | ||
> You can also assign parent container to any other container | ||
###### 2. Container.setParent() method. | ||
```typescript | ||
let parent = new Container(); | ||
let child = new Container(); | ||
const parent = new Container(); | ||
const child = new Container(); | ||
@@ -138,2 +161,10 @@ child.setParent(parent); | ||
###### 3. Via Container's constructor with options. | ||
```typescript | ||
const parent = new Container(); | ||
const child = new Container({ | ||
parent: parent | ||
}); | ||
``` | ||
### Using Factories | ||
@@ -140,0 +171,0 @@ ```typescript |
27265
6.68%417
4.77%216
16.76%