feat(di): provide two ways to create an injector, resolved and unresolved

Add two factory static functions to Injector: resolveAndCreate and
fromResolvedBindings.

We want to avoid resolution and flattening every time we create a new
injector. This commit allows the user to cache resolved bindings and
reuse them.
This commit is contained in:
Yegor Jbanov
2015-04-10 17:05:31 -07:00
parent 6c8398df9b
commit 4a961f4ecb
24 changed files with 160 additions and 127 deletions

View File

@ -276,10 +276,10 @@ export function bootstrap(appComponentType: Type,
}
function _createAppInjector(appComponentType: Type, bindings: List<Binding>, zone: VmTurnZone): Injector {
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
var mergedBindings = isPresent(bindings) ?
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
_injectorBindings(appComponentType);
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
return _rootInjector.createChild(mergedBindings);
return _rootInjector.resolveAndCreateChild(mergedBindings);
}

View File

@ -80,7 +80,7 @@ export class DynamicComponentLoader {
_componentAppInjector(location, injector, services) {
var inj = isPresent(injector) ? injector : location.injector;
return isPresent(services) ? inj.createChild(services) : inj;
return isPresent(services) ? inj.resolveAndCreateChild(services) : inj;
}
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {

View File

@ -153,7 +153,7 @@ export class AppView {
if (isPresent(componentDirective)) {
var injectables = componentDirective.annotation.injectables;
if (isPresent(injectables))
shadowDomAppInjector = appInjector.createChild(injectables);
shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables);
else {
shadowDomAppInjector = appInjector;
}

View File

@ -94,7 +94,9 @@ export class Binding {
for (var i = 0; i < bindings.length; i++) {
var unresolved = bindings[i];
var resolved;
if (unresolved instanceof Type) {
if (unresolved instanceof ResolvedBinding) {
resolved = unresolved; // ha-ha! I'm easily amused
} else if (unresolved instanceof Type) {
resolved = bind(unresolved).toClass(unresolved).resolve();
} else if (unresolved instanceof Binding) {
resolved = unresolved.resolve();

View File

@ -27,13 +27,39 @@ export class Injector {
_defaultBindings:boolean;
_asyncStrategy: _AsyncInjectorStrategy;
_syncStrategy:_SyncInjectorStrategy;
constructor(bindings:List, {parent=null, defaultBindings=false}={}) {
/**
* Creates/looks up factory functions and dependencies from binding
* declarations and flattens bindings into a single [List].
*/
static resolve(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):List<ResolvedBinding> {
var flatten = _flattenBindings(Binding.resolveAll(bindings), MapWrapper.create());
this._bindings = this._createListOfBindings(flatten);
return _createListOfBindings(flatten);
}
/**
* Resolves bindings and creates an injector based on those bindings. This function is slower than the
* corresponding [fromResolvedBindings] because it needs to resolve bindings. Prefer [fromResolvedBindings]
* in performance-critical code that creates lots of injectors.
*/
static resolveAndCreate(bindings:List/*<ResolvedBinding|Binding|Type|List>*/, {defaultBindings=false}={}) {
return new Injector(Injector.resolve(bindings), null, defaultBindings);
}
/**
* Creates an injector from previously resolved bindings. This bypasses a lot
* of computation and is the recommended way to construct injectors in
* performance-sensitive parts.
*/
static fromResolvedBindings(bindings:List<ResolvedBinding>, {defaultBindings=false}={}) {
return new Injector(bindings, null, defaultBindings);
}
constructor(bindings:List<ResolvedBinding>, parent:Injector, defaultBindings:boolean) {
this._bindings = bindings;
this._instances = this._createInstances();
this._parent = parent;
this._defaultBindings = defaultBindings;
this._asyncStrategy = new _AsyncInjectorStrategy(this);
this._syncStrategy = new _SyncInjectorStrategy(this);
}
@ -50,15 +76,12 @@ export class Injector {
return this._getByKey(Key.get(token), true, false, false);
}
createChild(bindings:List):Injector {
return new Injector(bindings, {parent: this});
resolveAndCreateChild(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):Injector {
return new Injector(Injector.resolve(bindings), this, false);
}
_createListOfBindings(flattenBindings):List {
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
return bindings;
createChildFromResolved(bindings:List<ResolvedBinding>):Injector {
return new Injector(bindings, this, false);
}
_createInstances():List {
@ -244,6 +267,14 @@ class _AsyncInjectorStrategy {
}
}
function _createListOfBindings(flattenBindings):List {
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
return bindings;
}
function _flattenBindings(bindings:List, res:Map) {
ListWrapper.forEach(bindings, function (b) {
if (b instanceof ResolvedBinding) {

View File

@ -110,8 +110,8 @@ function _getAppBindings() {
}
export function createTestInjector(bindings: List) {
var rootInjector = new Injector(_getRootBindings());
return rootInjector.createChild(ListWrapper.concat(_getAppBindings(), bindings));
var rootInjector = Injector.resolveAndCreate(_getRootBindings());
return rootInjector.resolveAndCreateChild(ListWrapper.concat(_getAppBindings(), bindings));
}
/**