Skip to content

Commit 4a961f4

Browse files
committed
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.
1 parent 6c8398d commit 4a961f4

24 files changed

Lines changed: 160 additions & 127 deletions

modules/angular2/docs/di/di.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Car {
4343
}
4444
}
4545
46-
var inj = new Injector([
46+
var inj = Injector.resolveAndCreate([
4747
bind(Car).toClass(Car),
4848
bind(Engine).toClass(Engine)
4949
]);
@@ -86,7 +86,7 @@ To avoid bugs make sure the registered objects have side-effect-free constructor
8686
Injectors are hierarchical.
8787

8888
```
89-
var child = injector.createChild([
89+
var child = injector.resolveAndCreateChild([
9090
bind(Engine).toClass(TurboEngine)
9191
]);
9292
@@ -99,21 +99,21 @@ var car = child.get(Car); // uses the Car binding from the parent injector and E
9999
You can bind to a class, a value, or a factory. It is also possible to alias existing bindings.
100100

101101
```
102-
var inj = new Injector([
102+
var inj = Injector.resolveAndCreate([
103103
bind(Car).toClass(Car),
104104
bind(Engine).toClass(Engine)
105105
]);
106106
107-
var inj = new Injector([
107+
var inj = Injector.resolveAndCreate([
108108
Car, // syntax sugar for bind(Car).toClass(Car)
109109
Engine
110110
]);
111111
112-
var inj = new Injector([
112+
var inj = Injector.resolveAndCreate([
113113
bind(Car).toValue(new Car(new Engine()))
114114
]);
115115
116-
var inj = new Injector([
116+
var inj = Injector.resolveAndCreate([
117117
bind(Car).toFactory((e) => new Car(e), [Engine]),
118118
bind(Engine).toFactory(() => new Engine())
119119
]);
@@ -122,7 +122,7 @@ var inj = new Injector([
122122
You can bind any token.
123123

124124
```
125-
var inj = new Injector([
125+
var inj = Injector.resolveAndCreate([
126126
bind(Car).toFactory((e) => new Car(), ["engine!"]),
127127
bind("engine!").toClass(Engine)
128128
]);
@@ -131,7 +131,7 @@ var inj = new Injector([
131131
If you want to alias an existing binding, you can do so using `toAlias`:
132132

133133
```
134-
var inj = new Injector([
134+
var inj = Injector.resolveAndCreate([
135135
bind(Engine).toClass(Engine),
136136
bind("engine!").toAlias(Engine)
137137
]);
@@ -152,7 +152,7 @@ The `someFactory` function does not have to know that it creates an object for `
152152
Injector can create binding on the fly if we enable default bindings.
153153

154154
```
155-
var inj = new Injector([], {defaultBindings: true});
155+
var inj = Injector.resolveAndCreate([], {defaultBindings: true});
156156
var car = inj.get(Car); //this works as if `bind(Car).toClass(Car)` and `bind(Engine).toClass(Engine)` were present.
157157
```
158158

@@ -226,7 +226,7 @@ class UserController {
226226
}
227227
}
228228
229-
var inj = new Injector([
229+
var inj = Injector.resolveAndCreate([
230230
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
231231
UserController
232232
])
@@ -252,7 +252,7 @@ class UserController {
252252
}
253253
}
254254
255-
var inj = new Injector([
255+
var inj = Injector.resolveAndCreate([
256256
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
257257
UserController
258258
])
@@ -276,7 +276,7 @@ class UserController {
276276
constructor(ul:UserList){}
277277
}
278278
279-
var inj = new Injector([UserList, UserController]);
279+
var inj = Injector.resolveAndCreate([UserList, UserController]);
280280
var ctrl:UserController = inj.get(UserController);
281281
```
282282

@@ -290,7 +290,7 @@ class UserController {
290290
constructor(@InjectPromise(UserList) ul){}
291291
}
292292
293-
var inj = new Injector([UserList, UserController]);
293+
var inj = Injector.resolveAndCreate([UserList, UserController]);
294294
var ctrl:UserController = inj.get(UserController);
295295
// UserController responsible for dealing with asynchrony.
296296
expect(ctrl.ul).toBePromise();
@@ -306,7 +306,7 @@ class UserController {
306306
constructor(ul:UserList){}
307307
}
308308
309-
var inj = new Injector([
309+
var inj = Injector.resolveAndCreate([
310310
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
311311
UserController
312312
]);
@@ -331,7 +331,7 @@ class UserController {
331331
constructor(@InjectPromise(UserList) ul){}
332332
}
333333
334-
var inj = new Injector([
334+
var inj = Injector.resolveAndCreate([
335335
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
336336
UserController
337337
]);
@@ -369,14 +369,14 @@ If we need a transient dependency, something that we want a new instance of ever
369369
We can create a child injector:
370370

371371
```
372-
var child = inj.createChild([MyClass]);
372+
var child = inj.resolveAndCreateChild([MyClass]);
373373
child.get(MyClass);
374374
```
375375

376376
Or we can register a factory function:
377377

378378
```
379-
var inj = new Injector([
379+
var inj = Injector.resolveAndCreate([
380380
bind('MyClassFactory').toFactory(dep => () => new MyClass(dep), [SomeDependency])
381381
]);
382382
@@ -393,7 +393,7 @@ expect(instance1).not.toBe(instance2);
393393
Most of the time we do not have to deal with keys.
394394

395395
```
396-
var inj = new Injector([
396+
var inj = Injector.resolveAndCreate([
397397
bind(Engine).toFactory(() => new TurboEngine()) //the passed in token Engine gets mapped to a key
398398
]);
399399
var engine = inj.get(Engine); //the passed in token Engine gets mapped to a key
@@ -404,7 +404,7 @@ Now, the same example, but with keys
404404
```
405405
var ENGINE_KEY = Key.get(Engine);
406406
407-
var inj = new Injector([
407+
var inj = Injector.resolveAndCreate([
408408
bind(ENGINE_KEY).toFactory(() => new TurboEngine()) // no mapping
409409
]);
410410
var engine = inj.get(ENGINE_KEY); // no mapping

modules/angular2/src/core/application.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,10 @@ export function bootstrap(appComponentType: Type,
276276
}
277277

278278
function _createAppInjector(appComponentType: Type, bindings: List<Binding>, zone: VmTurnZone): Injector {
279-
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
279+
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
280280
var mergedBindings = isPresent(bindings) ?
281281
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
282282
_injectorBindings(appComponentType);
283283
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
284-
return _rootInjector.createChild(mergedBindings);
284+
return _rootInjector.resolveAndCreateChild(mergedBindings);
285285
}

modules/angular2/src/core/compiler/dynamic_component_loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class DynamicComponentLoader {
8080

8181
_componentAppInjector(location, injector, services) {
8282
var inj = isPresent(injector) ? injector : location.injector;
83-
return isPresent(services) ? inj.createChild(services) : inj;
83+
return isPresent(services) ? inj.resolveAndCreateChild(services) : inj;
8484
}
8585

8686
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {

modules/angular2/src/core/compiler/view.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class AppView {
153153
if (isPresent(componentDirective)) {
154154
var injectables = componentDirective.annotation.injectables;
155155
if (isPresent(injectables))
156-
shadowDomAppInjector = appInjector.createChild(injectables);
156+
shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables);
157157
else {
158158
shadowDomAppInjector = appInjector;
159159
}

modules/angular2/src/di/binding.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ export class Binding {
9494
for (var i = 0; i < bindings.length; i++) {
9595
var unresolved = bindings[i];
9696
var resolved;
97-
if (unresolved instanceof Type) {
97+
if (unresolved instanceof ResolvedBinding) {
98+
resolved = unresolved; // ha-ha! I'm easily amused
99+
} else if (unresolved instanceof Type) {
98100
resolved = bind(unresolved).toClass(unresolved).resolve();
99101
} else if (unresolved instanceof Binding) {
100102
resolved = unresolved.resolve();

modules/angular2/src/di/injector.js

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,39 @@ export class Injector {
2727
_defaultBindings:boolean;
2828
_asyncStrategy: _AsyncInjectorStrategy;
2929
_syncStrategy:_SyncInjectorStrategy;
30-
constructor(bindings:List, {parent=null, defaultBindings=false}={}) {
30+
31+
/**
32+
* Creates/looks up factory functions and dependencies from binding
33+
* declarations and flattens bindings into a single [List].
34+
*/
35+
static resolve(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):List<ResolvedBinding> {
3136
var flatten = _flattenBindings(Binding.resolveAll(bindings), MapWrapper.create());
32-
this._bindings = this._createListOfBindings(flatten);
37+
return _createListOfBindings(flatten);
38+
}
39+
40+
/**
41+
* Resolves bindings and creates an injector based on those bindings. This function is slower than the
42+
* corresponding [fromResolvedBindings] because it needs to resolve bindings. Prefer [fromResolvedBindings]
43+
* in performance-critical code that creates lots of injectors.
44+
*/
45+
static resolveAndCreate(bindings:List/*<ResolvedBinding|Binding|Type|List>*/, {defaultBindings=false}={}) {
46+
return new Injector(Injector.resolve(bindings), null, defaultBindings);
47+
}
48+
49+
/**
50+
* Creates an injector from previously resolved bindings. This bypasses a lot
51+
* of computation and is the recommended way to construct injectors in
52+
* performance-sensitive parts.
53+
*/
54+
static fromResolvedBindings(bindings:List<ResolvedBinding>, {defaultBindings=false}={}) {
55+
return new Injector(bindings, null, defaultBindings);
56+
}
57+
58+
constructor(bindings:List<ResolvedBinding>, parent:Injector, defaultBindings:boolean) {
59+
this._bindings = bindings;
3360
this._instances = this._createInstances();
3461
this._parent = parent;
3562
this._defaultBindings = defaultBindings;
36-
3763
this._asyncStrategy = new _AsyncInjectorStrategy(this);
3864
this._syncStrategy = new _SyncInjectorStrategy(this);
3965
}
@@ -50,15 +76,12 @@ export class Injector {
5076
return this._getByKey(Key.get(token), true, false, false);
5177
}
5278

53-
createChild(bindings:List):Injector {
54-
return new Injector(bindings, {parent: this});
79+
resolveAndCreateChild(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):Injector {
80+
return new Injector(Injector.resolve(bindings), this, false);
5581
}
5682

57-
58-
_createListOfBindings(flattenBindings):List {
59-
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
60-
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
61-
return bindings;
83+
createChildFromResolved(bindings:List<ResolvedBinding>):Injector {
84+
return new Injector(bindings, this, false);
6285
}
6386

6487
_createInstances():List {
@@ -244,6 +267,14 @@ class _AsyncInjectorStrategy {
244267
}
245268
}
246269

270+
function _createListOfBindings(flattenBindings):List {
271+
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
272+
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
273+
return bindings;
274+
}
275+
276+
277+
247278
function _flattenBindings(bindings:List, res:Map) {
248279
ListWrapper.forEach(bindings, function (b) {
249280
if (b instanceof ResolvedBinding) {

modules/angular2/src/test_lib/test_injector.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ function _getAppBindings() {
110110
}
111111

112112
export function createTestInjector(bindings: List) {
113-
var rootInjector = new Injector(_getRootBindings());
114-
return rootInjector.createChild(ListWrapper.concat(_getAppBindings(), bindings));
113+
var rootInjector = Injector.resolveAndCreate(_getRootBindings());
114+
return rootInjector.resolveAndCreateChild(ListWrapper.concat(_getAppBindings(), bindings));
115115
}
116116

117117
/**

0 commit comments

Comments
 (0)